[lxc-devel] [lxd/master] push and relay mode for container copy/move

stgraber on Github lxc-bot at linuxcontainers.org
Mon Jul 3 05:37:50 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170703/d001a23a/attachment.bin>
-------------- next part --------------
From 5134b57faa22827e4272c96e08dbaec5a9124b2b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 3 Jul 2017 01:25:39 -0400
Subject: [PATCH 1/6] client: Don't live migrate stopped containers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 client/lxd_containers.go | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/client/lxd_containers.go b/client/lxd_containers.go
index 24a7f2d11..0e102d213 100644
--- a/client/lxd_containers.go
+++ b/client/lxd_containers.go
@@ -203,7 +203,6 @@ func (r *ProtocolLXD) CopyContainer(source ContainerServer, container api.Contai
 		ContainerPut: container.Writable(),
 	}
 	req.Source.BaseImage = container.Config["volatile.base_image"]
-	req.Source.Live = container.StatusCode == api.Running
 
 	// Process the copy arguments
 	if args != nil {
@@ -221,6 +220,10 @@ func (r *ProtocolLXD) CopyContainer(source ContainerServer, container api.Contai
 		req.Source.ContainerOnly = args.ContainerOnly
 	}
 
+	if req.Source.Live {
+		req.Source.Live = container.StatusCode == api.Running
+	}
+
 	// Optimization for the local copy case
 	if r == source {
 		// Local copy source fields

From 665f9013f70ae99a92c6c8c8bd08892b5dfd6cbf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 2 Jul 2017 20:42:33 -0400
Subject: [PATCH 2/6] Implement complete push migration
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 doc/api-extensions.md            |  4 +++
 lxd/api_1.0.go                   |  1 +
 lxd/container_post.go            | 16 +++++++++++
 lxd/container_snapshot.go        | 34 +++++++++++++++++++++-
 lxd/migrate.go                   | 61 ++++++++++++++++++++++++++++++++++++++++
 shared/api/container.go          | 12 ++++++++
 shared/api/container_snapshot.go |  5 ++--
 7 files changed, 130 insertions(+), 3 deletions(-)

diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index 4590b3eab..bc3dd2d61 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -288,3 +288,7 @@ uid/gid to use as the base.
 ## file\_symlinks
 This adds support for transfering symlinks through the file API.
 X-LXD-type can now be "symlink" with the request content being the target path.
+
+## container\_push\_target
+This adds the "target" field to POST /1.0/containers/NAME which can be
+used to have the source LXD host connect to the target during migration.
diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go
index 0c0ad691b..46f7b5459 100644
--- a/lxd/api_1.0.go
+++ b/lxd/api_1.0.go
@@ -111,6 +111,7 @@ func api10Get(d *Daemon, r *http.Request) Response {
 			"storage_lvm_lv_resizing",
 			"id_map_base",
 			"file_symlinks",
+			"container_push_target",
 		},
 		APIStatus:  "stable",
 		APIVersion: version.APIVersion,
diff --git a/lxd/container_post.go b/lxd/container_post.go
index ca884e3cd..69a697824 100644
--- a/lxd/container_post.go
+++ b/lxd/container_post.go
@@ -55,6 +55,22 @@ func containerPost(d *Daemon, r *http.Request) Response {
 		resources := map[string][]string{}
 		resources["containers"] = []string{name}
 
+		if req.Target != nil {
+			// Push mode
+			err := ws.ConnectTarget(*req.Target)
+			if err != nil {
+				return InternalError(err)
+			}
+
+			op, err := operationCreate(operationClassTask, resources, nil, ws.Do, nil, nil)
+			if err != nil {
+				return InternalError(err)
+			}
+
+			return OperationResponse(op)
+		}
+
+		// Pull mode
 		op, err := operationCreate(operationClassWebsocket, resources, ws.Metadata(), ws.Do, nil, ws.Connect)
 		if err != nil {
 			return InternalError(err)
diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go
index a4d2ace26..359d6c4cd 100644
--- a/lxd/container_snapshot.go
+++ b/lxd/container_snapshot.go
@@ -1,8 +1,10 @@
 package main
 
 import (
+	"bytes"
 	"encoding/json"
 	"fmt"
+	"io/ioutil"
 	"net/http"
 	"strconv"
 
@@ -196,13 +198,27 @@ func snapshotGet(sc container, name string) Response {
 }
 
 func snapshotPost(d *Daemon, r *http.Request, sc container, containerName string) Response {
+	body, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return InternalError(err)
+	}
+
+	rdr1 := ioutil.NopCloser(bytes.NewBuffer(body))
+	rdr2 := ioutil.NopCloser(bytes.NewBuffer(body))
+
 	raw := shared.Jmap{}
-	if err := json.NewDecoder(r.Body).Decode(&raw); err != nil {
+	if err := json.NewDecoder(rdr1).Decode(&raw); err != nil {
 		return BadRequest(err)
 	}
 
 	migration, err := raw.GetBool("migration")
 	if err == nil && migration {
+		req := api.ContainerPost{}
+		err = json.NewDecoder(rdr2).Decode(&req)
+		if err != nil {
+			return BadRequest(err)
+		}
+
 		ws, err := NewMigrationSource(sc, false, true)
 		if err != nil {
 			return SmartError(err)
@@ -211,6 +227,22 @@ func snapshotPost(d *Daemon, r *http.Request, sc container, containerName string
 		resources := map[string][]string{}
 		resources["containers"] = []string{containerName}
 
+		if req.Target != nil {
+			// Push mode
+			err := ws.ConnectTarget(*req.Target)
+			if err != nil {
+				return InternalError(err)
+			}
+
+			op, err := operationCreate(operationClassTask, resources, nil, ws.Do, nil, nil)
+			if err != nil {
+				return InternalError(err)
+			}
+
+			return OperationResponse(op)
+		}
+
+		// Pull mode
 		op, err := operationCreate(operationClassWebsocket, resources, ws.Metadata(), ws.Do, nil, ws.Connect)
 		if err != nil {
 			return InternalError(err)
diff --git a/lxd/migrate.go b/lxd/migrate.go
index 244ee1d73..16bcc6fa3 100644
--- a/lxd/migrate.go
+++ b/lxd/migrate.go
@@ -6,6 +6,8 @@
 package main
 
 import (
+	"crypto/x509"
+	"encoding/pem"
 	"fmt"
 	"io/ioutil"
 	"net/http"
@@ -21,6 +23,7 @@ import (
 	"gopkg.in/lxc/go-lxc.v2"
 
 	"github.com/lxc/lxd/shared"
+	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/logger"
 )
 
@@ -234,6 +237,64 @@ func (s *migrationSourceWs) Connect(op *operation, r *http.Request, w http.Respo
 	return nil
 }
 
+func (s *migrationSourceWs) ConnectTarget(target api.ContainerPostTarget) error {
+	var err error
+	var cert *x509.Certificate
+
+	if target.Certificate != "" {
+		certBlock, _ := pem.Decode([]byte(target.Certificate))
+		if certBlock == nil {
+			return fmt.Errorf("Invalid certificate")
+		}
+
+		cert, err = x509.ParseCertificate(certBlock.Bytes)
+		if err != nil {
+			return err
+		}
+	}
+
+	config, err := shared.GetTLSConfig("", "", "", cert)
+	if err != nil {
+		return err
+	}
+
+	dialer := websocket.Dialer{
+		TLSClientConfig: config,
+		NetDial:         shared.RFC3493Dialer,
+	}
+
+	for name, secret := range target.Websockets {
+		var conn **websocket.Conn
+
+		switch name {
+		case "control":
+			conn = &s.controlConn
+		case "fs":
+			conn = &s.fsConn
+		case "criu":
+			conn = &s.criuConn
+		default:
+			return fmt.Errorf("Unknown secret provided: %s", name)
+		}
+
+		query := url.Values{"secret": []string{secret}}
+
+		// The URL is a https URL to the operation, mangle to be a wss URL to the secret
+		wsUrl := fmt.Sprintf("wss://%s/websocket?%s", strings.TrimPrefix(target.Operation, "https://"), query.Encode())
+
+		wsConn, _, err := dialer.Dial(wsUrl, http.Header{})
+		if err != nil {
+			return err
+		}
+
+		*conn = wsConn
+	}
+
+	s.allConnected <- true
+
+	return nil
+}
+
 func writeActionScript(directory string, operation string, secret string) error {
 	script := fmt.Sprintf(`#!/bin/sh -e
 if [ "$CRTOOLS_SCRIPT_ACTION" = "post-dump" ]; then
diff --git a/shared/api/container.go b/shared/api/container.go
index a300a0731..58cac56cf 100644
--- a/shared/api/container.go
+++ b/shared/api/container.go
@@ -25,6 +25,18 @@ type ContainerPost struct {
 
 	// API extension: container_only_migration
 	ContainerOnly bool `json:"container_only" yaml:"container_only"`
+
+	// API extension: container_push_target
+	Target *ContainerPostTarget `json:"target" yaml:"target"`
+}
+
+// ContainerPostTarget represents the migration target host and operation
+//
+// API extension: container_push_target
+type ContainerPostTarget struct {
+	Certificate string            `json:"certificate" yaml:"certificate"`
+	Operation   string            `json:"operation,omitempty" yaml:"operation,omitempty"`
+	Websockets  map[string]string `json:"secrets,omitempty" yaml:"secrets,omitempty"`
 }
 
 // ContainerPut represents the modifiable fields of a LXD container
diff --git a/shared/api/container_snapshot.go b/shared/api/container_snapshot.go
index 17e5d3a8d..3f9b0fd6c 100644
--- a/shared/api/container_snapshot.go
+++ b/shared/api/container_snapshot.go
@@ -12,8 +12,9 @@ type ContainerSnapshotsPost struct {
 
 // ContainerSnapshotPost represents the fields required to rename/move a LXD container snapshot
 type ContainerSnapshotPost struct {
-	Name      string `json:"name" yaml:"name"`
-	Migration bool   `json:"migration" yaml:"migration"`
+	Name      string               `json:"name" yaml:"name"`
+	Migration bool                 `json:"migration" yaml:"migration"`
+	Target    *ContainerPostTarget `json:"target" yaml:"target"`
 }
 
 // ContainerSnapshot represents a LXD conainer snapshot

From e4bd65bbff29675a8e0e24c623c7038ab89e0bbe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 2 Jul 2017 01:57:01 -0400
Subject: [PATCH 3/6] client: Implement push and relay container copy
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 client/interfaces.go     |   6 +
 client/lxd_containers.go | 359 ++++++++++++++++++++++++++++++++++++++++++++---
 shared/network.go        |  54 +++++++
 3 files changed, 403 insertions(+), 16 deletions(-)

diff --git a/client/interfaces.go b/client/interfaces.go
index 410c4f726..d146c1ddf 100644
--- a/client/interfaces.go
+++ b/client/interfaces.go
@@ -240,12 +240,18 @@ type ContainerCopyArgs struct {
 
 	// If set, only the container will copied, its snapshots won't
 	ContainerOnly bool
+
+	// The transfer mode, can be "pull" (default), "push" or "relay"
+	Mode string
 }
 
 // The ContainerSnapshotCopyArgs struct is used to pass additional options during container copy
 type ContainerSnapshotCopyArgs struct {
 	// If set, the container will be renamed on copy
 	Name string
+
+	// The transfer mode, can be "pull" (default), "push" or "relay"
+	Mode string
 }
 
 // The ContainerExecArgs struct is used to pass additional options during container exec
diff --git a/client/lxd_containers.go b/client/lxd_containers.go
index 0e102d213..1cc305cf9 100644
--- a/client/lxd_containers.go
+++ b/client/lxd_containers.go
@@ -207,8 +207,28 @@ func (r *ProtocolLXD) CopyContainer(source ContainerServer, container api.Contai
 	// Process the copy arguments
 	if args != nil {
 		// Sanity checks
-		if args.ContainerOnly && !r.HasExtension("container_only_migration") {
-			return nil, fmt.Errorf("The server is missing the required \"container_only_migration\" API extension")
+		if args.ContainerOnly {
+			if !r.HasExtension("container_only_migration") {
+				return nil, fmt.Errorf("The target server is missing the required \"container_only_migration\" API extension")
+			}
+
+			if !source.HasExtension("container_only_migration") {
+				return nil, fmt.Errorf("The source server is missing the required \"container_only_migration\" API extension")
+			}
+		}
+
+		if shared.StringInSlice(args.Mode, []string{"push", "relay"}) {
+			if !r.HasExtension("container_push") {
+				return nil, fmt.Errorf("The target server is missing the required \"container_push\" API extension")
+			}
+
+			if !source.HasExtension("container_push") {
+				return nil, fmt.Errorf("The source server is missing the required \"container_push\" API extension")
+			}
+		}
+
+		if args.Mode == "push" && !source.HasExtension("container_push_target") {
+			return nil, fmt.Errorf("The source server is missing the required \"container_push_target\" API extension")
 		}
 
 		// Allow overriding the target name
@@ -250,19 +270,51 @@ func (r *ProtocolLXD) CopyContainer(source ContainerServer, container api.Contai
 		return &rop, nil
 	}
 
-	// Get source server connection information
-	info, err := source.GetConnectionInfo()
-	if err != nil {
-		return nil, err
-	}
-
-	// Get a source operation
+	// Source request
 	sourceReq := api.ContainerPost{
 		Migration:     true,
 		Live:          req.Source.Live,
 		ContainerOnly: req.Source.ContainerOnly,
 	}
 
+	// Push mode migration
+	if args != nil && args.Mode == "push" {
+		// Get target server connection information
+		info, err := r.GetConnectionInfo()
+		if err != nil {
+			return nil, err
+		}
+
+		// Create the container
+		req.Source.Type = "migration"
+		req.Source.Mode = "push"
+
+		op, err := r.CreateContainer(req)
+		if err != nil {
+			return nil, err
+		}
+
+		targetSecrets := map[string]string{}
+		for k, v := range op.Metadata {
+			targetSecrets[k] = v.(string)
+		}
+
+		// Prepare the source request
+		target := api.ContainerPostTarget{}
+		target.Operation = op.ID
+		target.Websockets = targetSecrets
+		target.Certificate = info.Certificate
+		sourceReq.Target = &target
+
+		return r.tryMigrateContainer(source, container.Name, sourceReq, info.Addresses)
+	}
+
+	// Get source server connection information
+	info, err := source.GetConnectionInfo()
+	if err != nil {
+		return nil, err
+	}
+
 	op, err := source.MigrateContainer(container.Name, sourceReq)
 	if err != nil {
 		return nil, err
@@ -273,7 +325,71 @@ func (r *ProtocolLXD) CopyContainer(source ContainerServer, container api.Contai
 		sourceSecrets[k] = v.(string)
 	}
 
-	// Migration source fields
+	// Relay mode migration
+	if args != nil && args.Mode == "relay" {
+		// Push copy source fields
+		req.Source.Type = "migration"
+		req.Source.Mode = "push"
+
+		// Start the process
+		targetOp, err := r.CreateContainer(req)
+		if err != nil {
+			return nil, err
+		}
+
+		// Extract the websockets
+		targetSecrets := map[string]string{}
+		for k, v := range targetOp.Metadata {
+			targetSecrets[k] = v.(string)
+		}
+
+		// Launch the relay
+		dones := []chan bool{}
+		conns := []*websocket.Conn{}
+
+		for name := range sourceSecrets {
+			sourceConn, err := source.GetOperationWebsocket(op.ID, sourceSecrets[name])
+			if err != nil {
+				return nil, err
+			}
+
+			targetConn, err := r.GetOperationWebsocket(targetOp.ID, targetSecrets[name])
+			if err != nil {
+				return nil, err
+			}
+
+			conns = append(conns, sourceConn)
+			conns = append(conns, targetConn)
+			dones = append(dones, shared.WebsocketProxy(sourceConn, targetConn))
+		}
+
+		// Wait for everything to be done
+		go func() {
+			for _, chDone := range dones {
+				<-chDone
+			}
+
+			for _, conn := range conns {
+				conn.Close()
+			}
+		}()
+
+		// Prepare a tracking operation
+		rop := RemoteOperation{
+			targetOp: targetOp,
+			chDone:   make(chan bool),
+		}
+
+		// Forward targetOp to remote op
+		go func() {
+			rop.err = rop.targetOp.Wait()
+			close(rop.chDone)
+		}()
+
+		return &rop, nil
+	}
+
+	// Pull mode migration
 	req.Source.Type = "migration"
 	req.Source.Mode = "pull"
 	req.Source.Operation = op.ID
@@ -310,6 +426,56 @@ func (r *ProtocolLXD) RenameContainer(name string, container api.ContainerPost)
 	return op, nil
 }
 
+func (r *ProtocolLXD) tryMigrateContainer(source ContainerServer, name string, req api.ContainerPost, urls []string) (*RemoteOperation, error) {
+	if len(urls) == 0 {
+		return nil, fmt.Errorf("The target server isn't listening on the network")
+	}
+
+	rop := RemoteOperation{
+		chDone: make(chan bool),
+	}
+
+	operation := req.Target.Operation
+
+	// Forward targetOp to remote op
+	go func() {
+		success := false
+		errors := []string{}
+		for _, serverURL := range urls {
+			req.Target.Operation = fmt.Sprintf("%s/1.0/operations/%s", serverURL, operation)
+
+			op, err := source.MigrateContainer(name, req)
+			if err != nil {
+				errors = append(errors, fmt.Sprintf("%s: %v", serverURL, err))
+				continue
+			}
+
+			rop.targetOp = op
+
+			for _, handler := range rop.handlers {
+				rop.targetOp.AddHandler(handler)
+			}
+
+			err = rop.targetOp.Wait()
+			if err != nil {
+				errors = append(errors, fmt.Sprintf("%s: %v", serverURL, err))
+				continue
+			}
+
+			success = true
+			break
+		}
+
+		if !success {
+			rop.err = fmt.Errorf("Failed container migration:\n - %s", strings.Join(errors, "\n - "))
+		}
+
+		close(rop.chDone)
+	}()
+
+	return &rop, nil
+}
+
 // MigrateContainer requests that LXD prepares for a container migration
 func (r *ProtocolLXD) MigrateContainer(name string, container api.ContainerPost) (*Operation, error) {
 	if container.ContainerOnly {
@@ -695,6 +861,21 @@ func (r *ProtocolLXD) CopyContainerSnapshot(source ContainerServer, snapshot api
 
 	// Process the copy arguments
 	if args != nil {
+		// Sanity checks
+		if shared.StringInSlice(args.Mode, []string{"push", "relay"}) {
+			if !r.HasExtension("container_push") {
+				return nil, fmt.Errorf("The target server is missing the required \"container_push\" API extension")
+			}
+
+			if !source.HasExtension("container_push") {
+				return nil, fmt.Errorf("The source server is missing the required \"container_push\" API extension")
+			}
+		}
+
+		if args.Mode == "push" && !source.HasExtension("container_push_target") {
+			return nil, fmt.Errorf("The source server is missing the required \"container_push_target\" API extension")
+		}
+
 		// Allow overriding the target name
 		if args.Name != "" {
 			req.Name = args.Name
@@ -727,17 +908,49 @@ func (r *ProtocolLXD) CopyContainerSnapshot(source ContainerServer, snapshot api
 		return &rop, nil
 	}
 
+	// Source request
+	sourceReq := api.ContainerSnapshotPost{
+		Migration: true,
+	}
+
+	// Push mode migration
+	if args != nil && args.Mode == "push" {
+		// Get target server connection information
+		info, err := r.GetConnectionInfo()
+		if err != nil {
+			return nil, err
+		}
+
+		// Create the container
+		req.Source.Type = "migration"
+		req.Source.Mode = "push"
+
+		op, err := r.CreateContainer(req)
+		if err != nil {
+			return nil, err
+		}
+
+		targetSecrets := map[string]string{}
+		for k, v := range op.Metadata {
+			targetSecrets[k] = v.(string)
+		}
+
+		// Prepare the source request
+		target := api.ContainerPostTarget{}
+		target.Operation = op.ID
+		target.Websockets = targetSecrets
+		target.Certificate = info.Certificate
+		sourceReq.Target = &target
+
+		return r.tryMigrateContainerSnapshot(source, cName, sName, sourceReq, info.Addresses)
+	}
+
 	// Get source server connection information
 	info, err := source.GetConnectionInfo()
 	if err != nil {
 		return nil, err
 	}
 
-	// Get a source operation
-	sourceReq := api.ContainerSnapshotPost{
-		Migration: true,
-	}
-
 	op, err := source.MigrateContainerSnapshot(cName, sName, sourceReq)
 	if err != nil {
 		return nil, err
@@ -748,7 +961,71 @@ func (r *ProtocolLXD) CopyContainerSnapshot(source ContainerServer, snapshot api
 		sourceSecrets[k] = v.(string)
 	}
 
-	// Migration source fields
+	// Relay mode migration
+	if args != nil && args.Mode == "relay" {
+		// Push copy source fields
+		req.Source.Type = "migration"
+		req.Source.Mode = "push"
+
+		// Start the process
+		targetOp, err := r.CreateContainer(req)
+		if err != nil {
+			return nil, err
+		}
+
+		// Extract the websockets
+		targetSecrets := map[string]string{}
+		for k, v := range targetOp.Metadata {
+			targetSecrets[k] = v.(string)
+		}
+
+		// Launch the relay
+		dones := []chan bool{}
+		conns := []*websocket.Conn{}
+
+		for name := range sourceSecrets {
+			sourceConn, err := source.GetOperationWebsocket(op.ID, sourceSecrets[name])
+			if err != nil {
+				return nil, err
+			}
+
+			targetConn, err := r.GetOperationWebsocket(targetOp.ID, targetSecrets[name])
+			if err != nil {
+				return nil, err
+			}
+
+			conns = append(conns, sourceConn)
+			conns = append(conns, targetConn)
+			dones = append(dones, shared.WebsocketProxy(sourceConn, targetConn))
+		}
+
+		// Wait for everything to be done
+		go func() {
+			for _, chDone := range dones {
+				<-chDone
+			}
+
+			for _, conn := range conns {
+				conn.Close()
+			}
+		}()
+
+		// Prepare a tracking operation
+		rop := RemoteOperation{
+			targetOp: targetOp,
+			chDone:   make(chan bool),
+		}
+
+		// Forward targetOp to remote op
+		go func() {
+			rop.err = rop.targetOp.Wait()
+			close(rop.chDone)
+		}()
+
+		return &rop, nil
+	}
+
+	// Pull mode migration
 	req.Source.Type = "migration"
 	req.Source.Mode = "pull"
 	req.Source.Operation = op.ID
@@ -774,6 +1051,56 @@ func (r *ProtocolLXD) RenameContainerSnapshot(containerName string, name string,
 	return op, nil
 }
 
+func (r *ProtocolLXD) tryMigrateContainerSnapshot(source ContainerServer, containerName string, name string, req api.ContainerSnapshotPost, urls []string) (*RemoteOperation, error) {
+	if len(urls) == 0 {
+		return nil, fmt.Errorf("The target server isn't listening on the network")
+	}
+
+	rop := RemoteOperation{
+		chDone: make(chan bool),
+	}
+
+	operation := req.Target.Operation
+
+	// Forward targetOp to remote op
+	go func() {
+		success := false
+		errors := []string{}
+		for _, serverURL := range urls {
+			req.Target.Operation = fmt.Sprintf("%s/1.0/operations/%s", serverURL, operation)
+
+			op, err := source.MigrateContainerSnapshot(containerName, name, req)
+			if err != nil {
+				errors = append(errors, fmt.Sprintf("%s: %v", serverURL, err))
+				continue
+			}
+
+			rop.targetOp = op
+
+			for _, handler := range rop.handlers {
+				rop.targetOp.AddHandler(handler)
+			}
+
+			err = rop.targetOp.Wait()
+			if err != nil {
+				errors = append(errors, fmt.Sprintf("%s: %v", serverURL, err))
+				continue
+			}
+
+			success = true
+			break
+		}
+
+		if !success {
+			rop.err = fmt.Errorf("Failed container migration:\n - %s", strings.Join(errors, "\n - "))
+		}
+
+		close(rop.chDone)
+	}()
+
+	return &rop, nil
+}
+
 // MigrateContainerSnapshot requests that LXD prepares for a snapshot migration
 func (r *ProtocolLXD) MigrateContainerSnapshot(containerName string, name string, container api.ContainerSnapshotPost) (*Operation, error) {
 	// Sanity check
diff --git a/shared/network.go b/shared/network.go
index 51f1d796a..4cf7af81b 100644
--- a/shared/network.go
+++ b/shared/network.go
@@ -230,6 +230,60 @@ func WebsocketRecvStream(w io.Writer, conn *websocket.Conn) chan bool {
 	return ch
 }
 
+func WebsocketProxy(source *websocket.Conn, target *websocket.Conn) chan bool {
+	forward := func(in *websocket.Conn, out *websocket.Conn, ch chan bool) {
+		for {
+			mt, r, err := in.NextReader()
+			if mt == websocket.CloseMessage {
+				logger.Debugf("Got close message for reader")
+				break
+			}
+
+			if mt == websocket.TextMessage {
+				logger.Debugf("got message barrier")
+				break
+			}
+
+			if err != nil {
+				logger.Debugf("Got error getting next reader %s", err)
+				break
+			}
+
+			w, err := out.NextWriter(websocket.BinaryMessage)
+			if err != nil {
+				logger.Debugf("Got error getting next writer %s", err)
+				break
+			}
+
+			_, err = io.Copy(w, r)
+			w.Close()
+			if err != nil {
+				logger.Debugf("Got err writing %s", err)
+				break
+			}
+		}
+		out.WriteMessage(websocket.TextMessage, []byte{})
+
+		ch <- true
+	}
+
+	chSend := make(chan bool)
+	go forward(source, target, chSend)
+
+	chRecv := make(chan bool)
+	go forward(target, source, chRecv)
+
+	ch := make(chan bool)
+	go func() {
+		<-chSend
+		<-chRecv
+
+		ch <- true
+	}()
+
+	return ch
+}
+
 func defaultReader(conn *websocket.Conn, r io.ReadCloser, readDone chan<- bool) {
 	/* For now, we don't need to adjust buffer sizes in
 	* WebsocketMirror, since it's used for interactive things like

From cb33006b3f23c54b93a605ec8821e11613432882 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 2 Jul 2017 19:32:40 -0400
Subject: [PATCH 4/6] lxc/copy: Make transfer mode configurable
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Closes #553

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxc/copy.go | 16 +++++++++++++---
 lxc/move.go | 10 +++++++++-
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/lxc/copy.go b/lxc/copy.go
index f5388051b..befcfc15a 100644
--- a/lxc/copy.go
+++ b/lxc/copy.go
@@ -16,6 +16,7 @@ type copyCmd struct {
 	confArgs      configList
 	ephem         bool
 	containerOnly bool
+	mode          string
 }
 
 func (c *copyCmd) showByDefault() bool {
@@ -36,10 +37,11 @@ func (c *copyCmd) flags() {
 	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"))
+	gnuflag.StringVar(&c.mode, "mode", "pull", i18n.G("Transfer mode. One of pull (default), push or relay."))
 	gnuflag.BoolVar(&c.containerOnly, "container-only", false, i18n.G("Copy the container without its snapshots"))
 }
 
-func (c *copyCmd) copyContainer(conf *config.Config, sourceResource string, destResource string, keepVolatile bool, ephemeral int, stateful bool, containerOnly bool) error {
+func (c *copyCmd) copyContainer(conf *config.Config, sourceResource string, destResource string, keepVolatile bool, ephemeral int, stateful bool, containerOnly bool, mode string) error {
 	// Parse the source
 	sourceRemote, sourceName, err := conf.ParseRemote(sourceResource)
 	if err != nil {
@@ -86,6 +88,7 @@ func (c *copyCmd) copyContainer(conf *config.Config, sourceResource string, dest
 		// Prepare the container creation request
 		args := lxd.ContainerSnapshotCopyArgs{
 			Name: destName,
+			Mode: mode,
 		}
 
 		// Copy of a snapshot into a new container
@@ -138,6 +141,7 @@ func (c *copyCmd) copyContainer(conf *config.Config, sourceResource string, dest
 			Name:          destName,
 			Live:          stateful,
 			ContainerOnly: containerOnly,
+			Mode:          mode,
 		}
 
 		// Copy of a container into a new container
@@ -235,11 +239,17 @@ func (c *copyCmd) run(conf *config.Config, args []string) error {
 		ephem = 1
 	}
 
+	// Parse the mode
+	mode := "pull"
+	if c.mode != "" {
+		mode = c.mode
+	}
+
 	// If not target name is specified, one will be chosed by the server
 	if len(args) < 2 {
-		return c.copyContainer(conf, args[0], "", false, ephem, false, c.containerOnly)
+		return c.copyContainer(conf, args[0], "", false, ephem, false, c.containerOnly, mode)
 	}
 
 	// Normal copy with a pre-determined name
-	return c.copyContainer(conf, args[0], args[1], false, ephem, false, c.containerOnly)
+	return c.copyContainer(conf, args[0], args[1], false, ephem, false, c.containerOnly, mode)
 }
diff --git a/lxc/move.go b/lxc/move.go
index 9fcb3d229..6d867070d 100644
--- a/lxc/move.go
+++ b/lxc/move.go
@@ -12,6 +12,7 @@ import (
 
 type moveCmd struct {
 	containerOnly bool
+	mode          string
 }
 
 func (c *moveCmd) showByDefault() bool {
@@ -36,6 +37,7 @@ lxc move <container>/<old snapshot name> <container>/<new snapshot name>
 
 func (c *moveCmd) flags() {
 	gnuflag.BoolVar(&c.containerOnly, "container-only", false, i18n.G("Move the container without its snapshots"))
+	gnuflag.StringVar(&c.mode, "mode", "pull", i18n.G("Transfer mode. One of pull (default), push or relay."))
 }
 
 func (c *moveCmd) run(conf *config.Config, args []string) error {
@@ -43,6 +45,12 @@ func (c *moveCmd) run(conf *config.Config, args []string) error {
 		return errArgs
 	}
 
+	// Parse the mode
+	mode := "pull"
+	if c.mode != "" {
+		mode = c.mode
+	}
+
 	sourceRemote, sourceName, err := conf.ParseRemote(args[0])
 	if err != nil {
 		return err
@@ -90,7 +98,7 @@ func (c *moveCmd) run(conf *config.Config, args []string) error {
 
 	// 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.
-	err = cpy.copyContainer(conf, args[0], args[1], true, -1, true, c.containerOnly)
+	err = cpy.copyContainer(conf, args[0], args[1], true, -1, true, c.containerOnly, mode)
 	if err != nil {
 		return err
 	}

From 9e10964fabad07c7a2bd28efaef745734925d087 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 3 Jul 2017 01:27:20 -0400
Subject: [PATCH 5/6] tests: Add test for push and relay mode
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 test/suites/migration.sh | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/test/suites/migration.sh b/test/suites/migration.sh
index 6a75cf4ec..afd1859cb 100644
--- a/test/suites/migration.sh
+++ b/test/suites/migration.sh
@@ -85,7 +85,7 @@ migration() {
     [ -d "${lxd2_dir}/snapshots/nonlive/snap0/rootfs/bin" ]
   fi
 
-  lxc_remote copy l2:nonlive l1:nonlive2
+  lxc_remote copy l2:nonlive l1:nonlive2 --mode=push
   # This line exists so that the container's storage volume is mounted when we
   # perform existence check for various files.
   lxc_remote start l2:nonlive
@@ -100,7 +100,7 @@ migration() {
     [ -d "${LXD_DIR}/snapshots/nonlive2/snap0/rootfs/bin" ]
   fi
 
-  lxc_remote copy l1:nonlive2/snap0 l2:nonlive3
+  lxc_remote copy l1:nonlive2/snap0 l2:nonlive3 --mode=relay
   # FIXME: make this backend agnostic
   if [ "$lxd2_backend" != "lvm" ]; then
     [ -d "${lxd2_dir}/containers/nonlive3/rootfs/bin" ]
@@ -108,7 +108,7 @@ migration() {
   lxc_remote delete l2:nonlive3 --force
 
   lxc_remote stop l2:nonlive
-  lxc_remote copy l2:nonlive l2:nonlive2
+  lxc_remote copy l2:nonlive l2:nonlive2 --mode=push
   # should have the same base image tag
   [ "$(lxc_remote config get l2:nonlive volatile.base_image)" = "$(lxc_remote config get l2:nonlive2 volatile.base_image)" ]
   # check that nonlive2 has a new addr in volatile
@@ -158,7 +158,8 @@ migration() {
   lxc_remote delete l2:udssr
 
   # Remote container only move.
-  lxc_remote move l1:cccp l2:udssr --container-only
+  lxc_remote list l1:cccp
+  lxc_remote move l1:cccp l2:udssr --container-only --mode=relay
   ! lxc_remote info l1:cccp
   [ "$(lxc_remote info l2:udssr | grep -c snap)" -eq 0 ]
   lxc_remote delete l2:udssr
@@ -168,7 +169,7 @@ migration() {
   lxc_remote snapshot l1:cccp
 
   # Remote container with snapshots move.
-  lxc_remote move l1:cccp l2:udssr
+  lxc_remote move l1:cccp l2:udssr --mode=push
   ! lxc_remote info l1:cccp
   [ "$(lxc_remote info l2:udssr | grep -c snap)" -eq 2 ]
   lxc_remote delete l2:udssr
@@ -179,7 +180,7 @@ migration() {
   lxc snapshot cccp
 
   # Local container with snapshots move.
-  lxc move cccp udssr
+  lxc move cccp udssr --mode=pull
   ! lxc info cccp
   [ "$(lxc info udssr | grep -c snap)" -eq 2 ]
   lxc delete udssr

From edc99c229e475e2f7edd2a15bb7ef822fe18f871 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 3 Jul 2017 01:12:12 -0400
Subject: [PATCH 6/6] i18n: Update templates
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 po/de.po   | 28 ++++++++++++++++------------
 po/el.po   | 28 ++++++++++++++++------------
 po/fr.po   | 28 ++++++++++++++++------------
 po/it.po   | 28 ++++++++++++++++------------
 po/ja.po   | 28 ++++++++++++++++------------
 po/lxd.pot | 28 ++++++++++++++++------------
 po/nl.po   | 28 ++++++++++++++++------------
 po/ru.po   | 28 ++++++++++++++++------------
 po/sr.po   | 28 ++++++++++++++++------------
 po/sv.po   | 28 ++++++++++++++++------------
 po/tr.po   | 28 ++++++++++++++++------------
 11 files changed, 176 insertions(+), 132 deletions(-)

diff --git a/po/de.po b/po/de.po
index d5f1a1714..50bdb872e 100644
--- a/po/de.po
+++ b/po/de.po
@@ -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: 2017-07-02 01:58-0400\n"
+"POT-Creation-Date: 2017-07-03 01:12-0400\n"
 "PO-Revision-Date: 2017-02-14 17:11+0000\n"
 "Last-Translator: Tim Rose <tim at netlope.de>\n"
 "Language-Team: German <https://hosted.weblate.org/projects/linux-containers/"
@@ -347,7 +347,7 @@ msgstr ""
 msgid "Commands:"
 msgstr ""
 
-#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:136 lxc/init.go:137
+#: lxc/copy.go:34 lxc/copy.go:35 lxc/init.go:136 lxc/init.go:137
 #, fuzzy
 msgid "Config key/value to apply to the new container"
 msgstr "kann nicht zum selben Container Namen kopieren"
@@ -366,7 +366,7 @@ msgstr ""
 msgid "Container name is mandatory"
 msgstr ""
 
-#: lxc/copy.go:220 lxc/init.go:314
+#: lxc/copy.go:224 lxc/init.go:314
 #, c-format
 msgid "Container name is: %s"
 msgstr ""
@@ -380,7 +380,7 @@ msgstr "Abbild mit Fingerabdruck %s importiert\n"
 msgid "Copy aliases from source"
 msgstr "Kopiere Aliasse von der Quelle"
 
-#: lxc/copy.go:39
+#: lxc/copy.go:41
 #, fuzzy
 msgid "Copy the container without its snapshots"
 msgstr "Herunterfahren des Containers erzwingen."
@@ -480,7 +480,7 @@ msgstr ""
 msgid "Environment:"
 msgstr ""
 
-#: lxc/copy.go:37 lxc/copy.go:38 lxc/init.go:140 lxc/init.go:141
+#: lxc/copy.go:38 lxc/copy.go:39 lxc/init.go:140 lxc/init.go:141
 msgid "Ephemeral container"
 msgstr "Flüchtiger Container"
 
@@ -516,7 +516,7 @@ msgstr ""
 msgid "Failed to generate 'lxc.1': %v"
 msgstr ""
 
-#: lxc/copy.go:215
+#: lxc/copy.go:219
 #, fuzzy
 msgid "Failed to get the new container name"
 msgstr "kann nicht zum selben Container Namen kopieren"
@@ -703,7 +703,7 @@ msgid "More than one file to download, but target is not a directory"
 msgstr ""
 "Mehr als eine Datei herunterzuladen, aber das Ziel ist kein Verzeichnis"
 
-#: lxc/move.go:38
+#: lxc/move.go:39
 #, fuzzy
 msgid "Move the container without its snapshots"
 msgstr "Herunterfahren des Containers erzwingen."
@@ -890,7 +890,7 @@ msgstr "Profil %s gelöscht\n"
 msgid "Profile %s removed from %s"
 msgstr "Gerät %s wurde von %s entfernt\n"
 
-#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:138 lxc/init.go:139
+#: lxc/copy.go:36 lxc/copy.go:37 lxc/init.go:138 lxc/init.go:139
 #, fuzzy
 msgid "Profile to apply to the new container"
 msgstr "kann nicht zum selben Container Namen kopieren"
@@ -1201,7 +1201,11 @@ msgstr ""
 msgid "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr ""
 
-#: lxc/copy.go:189
+#: lxc/copy.go:40 lxc/move.go:40
+msgid "Transfer mode. One of pull (default), push or relay."
+msgstr ""
+
+#: lxc/copy.go:193
 #, fuzzy, c-format
 msgid "Transfering container: %s"
 msgstr "kann nicht zum selben Container Namen kopieren"
@@ -1382,7 +1386,7 @@ msgstr ""
 "Um das Server Passwort zur authentifizierung zu setzen:\n"
 "\tlxc config set core.trust_password blah\n"
 
-#: lxc/copy.go:26
+#: lxc/copy.go:27
 #, fuzzy
 msgid ""
 "Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] "
@@ -1777,7 +1781,7 @@ msgid ""
 "    Only show log message."
 msgstr ""
 
-#: lxc/move.go:22
+#: lxc/move.go:23
 #, fuzzy
 msgid ""
 "Usage: lxc move [<remote>:]<container>[/<snapshot>] [<remote>:][<container>[/"
@@ -2192,7 +2196,7 @@ msgstr ""
 msgid "You can't pass -t or -T at the same time as --mode"
 msgstr "kann nicht zum selben Container Namen kopieren"
 
-#: lxc/copy.go:57
+#: lxc/copy.go:59
 #, fuzzy
 msgid "You must specify a source container name"
 msgstr "der Name des Ursprung Containers muss angegeben werden"
diff --git a/po/el.po b/po/el.po
index 221369446..00845b4c0 100644
--- a/po/el.po
+++ b/po/el.po
@@ -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: 2017-07-02 01:58-0400\n"
+"POT-Creation-Date: 2017-07-03 01:12-0400\n"
 "PO-Revision-Date: 2017-02-14 08:00+0000\n"
 "Last-Translator: Simos Xenitellis <simos.65 at gmail.com>\n"
 "Language-Team: Greek <https://hosted.weblate.org/projects/linux-containers/"
@@ -259,7 +259,7 @@ msgstr ""
 msgid "Commands:"
 msgstr ""
 
-#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:136 lxc/init.go:137
+#: lxc/copy.go:34 lxc/copy.go:35 lxc/init.go:136 lxc/init.go:137
 msgid "Config key/value to apply to the new container"
 msgstr ""
 
@@ -277,7 +277,7 @@ msgstr ""
 msgid "Container name is mandatory"
 msgstr ""
 
-#: lxc/copy.go:220 lxc/init.go:314
+#: lxc/copy.go:224 lxc/init.go:314
 #, c-format
 msgid "Container name is: %s"
 msgstr ""
@@ -291,7 +291,7 @@ msgstr ""
 msgid "Copy aliases from source"
 msgstr ""
 
-#: lxc/copy.go:39
+#: lxc/copy.go:41
 msgid "Copy the container without its snapshots"
 msgstr ""
 
@@ -387,7 +387,7 @@ msgstr ""
 msgid "Environment:"
 msgstr ""
 
-#: lxc/copy.go:37 lxc/copy.go:38 lxc/init.go:140 lxc/init.go:141
+#: lxc/copy.go:38 lxc/copy.go:39 lxc/init.go:140 lxc/init.go:141
 msgid "Ephemeral container"
 msgstr ""
 
@@ -423,7 +423,7 @@ msgstr ""
 msgid "Failed to generate 'lxc.1': %v"
 msgstr ""
 
-#: lxc/copy.go:215
+#: lxc/copy.go:219
 msgid "Failed to get the new container name"
 msgstr ""
 
@@ -604,7 +604,7 @@ msgstr ""
 msgid "More than one file to download, but target is not a directory"
 msgstr ""
 
-#: lxc/move.go:38
+#: lxc/move.go:39
 msgid "Move the container without its snapshots"
 msgstr ""
 
@@ -783,7 +783,7 @@ msgstr ""
 msgid "Profile %s removed from %s"
 msgstr ""
 
-#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:138 lxc/init.go:139
+#: lxc/copy.go:36 lxc/copy.go:37 lxc/init.go:138 lxc/init.go:139
 msgid "Profile to apply to the new container"
 msgstr ""
 
@@ -1079,7 +1079,11 @@ msgstr ""
 msgid "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr ""
 
-#: lxc/copy.go:189
+#: lxc/copy.go:40 lxc/move.go:40
+msgid "Transfer mode. One of pull (default), push or relay."
+msgstr ""
+
+#: lxc/copy.go:193
 #, c-format
 msgid "Transfering container: %s"
 msgstr ""
@@ -1221,7 +1225,7 @@ msgid ""
 "    Will set the server's trust password to blah."
 msgstr ""
 
-#: lxc/copy.go:26
+#: lxc/copy.go:27
 msgid ""
 "Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] "
 "[--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...] [--"
@@ -1555,7 +1559,7 @@ msgid ""
 "    Only show log message."
 msgstr ""
 
-#: lxc/move.go:22
+#: lxc/move.go:23
 msgid ""
 "Usage: lxc move [<remote>:]<container>[/<snapshot>] [<remote>:][<container>[/"
 "<snapshot>]] [--container-only]\n"
@@ -1908,7 +1912,7 @@ msgstr ""
 msgid "You can't pass -t or -T at the same time as --mode"
 msgstr ""
 
-#: lxc/copy.go:57
+#: lxc/copy.go:59
 msgid "You must specify a source container name"
 msgstr ""
 
diff --git a/po/fr.po b/po/fr.po
index dc131c398..17426e804 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -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: 2017-07-02 01:58-0400\n"
+"POT-Creation-Date: 2017-07-03 01:12-0400\n"
 "PO-Revision-Date: 2017-06-07 15:24+0000\n"
 "Last-Translator: Stéphane Graber <stgraber at stgraber.org>\n"
 "Language-Team: French <https://hosted.weblate.org/projects/linux-containers/"
@@ -339,7 +339,7 @@ msgstr "Colonnes"
 msgid "Commands:"
 msgstr "Commandes:"
 
-#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:136 lxc/init.go:137
+#: lxc/copy.go:34 lxc/copy.go:35 lxc/init.go:136 lxc/init.go:137
 msgid "Config key/value to apply to the new container"
 msgstr "Clé/valeur de configuration à appliquer au nouveau conteneur"
 
@@ -357,7 +357,7 @@ msgstr "Connexion refusée ; LXD est-il actif ?"
 msgid "Container name is mandatory"
 msgstr "Le nom du conteneur est obligatoire"
 
-#: lxc/copy.go:220 lxc/init.go:314
+#: lxc/copy.go:224 lxc/init.go:314
 #, c-format
 msgid "Container name is: %s"
 msgstr "Le nom du conteneur est : %s"
@@ -371,7 +371,7 @@ msgstr "Conteneur publié avec l'empreinte : %s"
 msgid "Copy aliases from source"
 msgstr "Copier les alias depuis la source"
 
-#: lxc/copy.go:39
+#: lxc/copy.go:41
 #, fuzzy
 msgid "Copy the container without its snapshots"
 msgstr "Forcer le conteneur à s'arrêter"
@@ -468,7 +468,7 @@ msgstr "Variable d'environnement (de la forme HOME=/home/foo) à positionner"
 msgid "Environment:"
 msgstr "Environnement :"
 
-#: lxc/copy.go:37 lxc/copy.go:38 lxc/init.go:140 lxc/init.go:141
+#: lxc/copy.go:38 lxc/copy.go:39 lxc/init.go:140 lxc/init.go:141
 msgid "Ephemeral container"
 msgstr "Conteneur éphémère"
 
@@ -504,7 +504,7 @@ msgstr "Échec lors de la génération de 'lxc.%s.1': %v"
 msgid "Failed to generate 'lxc.1': %v"
 msgstr "Échec lors de la génération de 'lxc.1': %v"
 
-#: lxc/copy.go:215
+#: lxc/copy.go:219
 #, fuzzy
 msgid "Failed to get the new container name"
 msgstr "Profil à appliquer au nouveau conteneur"
@@ -693,7 +693,7 @@ msgid "More than one file to download, but target is not a directory"
 msgstr ""
 "Plusieurs fichiers à télécharger, mais la destination n'est pas un dossier"
 
-#: lxc/move.go:38
+#: lxc/move.go:39
 #, fuzzy
 msgid "Move the container without its snapshots"
 msgstr "Forcer le conteneur à s'arrêter"
@@ -875,7 +875,7 @@ msgstr "Profil %s supprimé"
 msgid "Profile %s removed from %s"
 msgstr "Profil %s supprimé de %s"
 
-#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:138 lxc/init.go:139
+#: lxc/copy.go:36 lxc/copy.go:37 lxc/init.go:138 lxc/init.go:139
 msgid "Profile to apply to the new container"
 msgstr "Profil à appliquer au nouveau conteneur"
 
@@ -1188,7 +1188,11 @@ msgid "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr ""
 "Pour démarrer votre premier conteneur, essayer : lxc launch ubuntu:16.04"
 
-#: lxc/copy.go:189
+#: lxc/copy.go:40 lxc/move.go:40
+msgid "Transfer mode. One of pull (default), push or relay."
+msgstr ""
+
+#: lxc/copy.go:193
 #, fuzzy, c-format
 msgid "Transfering container: %s"
 msgstr "Transfert de l'image : %s"
@@ -1390,7 +1394,7 @@ msgstr ""
 "Pour positionner le mot de passe de confiance du serveur :\n"
 "    lxc config set core.trust_password blah"
 
-#: lxc/copy.go:26
+#: lxc/copy.go:27
 #, fuzzy
 msgid ""
 "Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] "
@@ -1959,7 +1963,7 @@ msgstr ""
 "Exemple :\n"
 "lxc monitor --type=logging"
 
-#: lxc/move.go:22
+#: lxc/move.go:23
 #, fuzzy
 msgid ""
 "Usage: lxc move [<remote>:]<container>[/<snapshot>] [<remote>:][<container>[/"
@@ -2472,7 +2476,7 @@ msgstr "Il est impossible de passer -t et -T simultanément"
 msgid "You can't pass -t or -T at the same time as --mode"
 msgstr "impossible de copier vers le même nom de conteneur"
 
-#: lxc/copy.go:57
+#: lxc/copy.go:59
 #, fuzzy
 msgid "You must specify a source container name"
 msgstr "vous devez spécifier un nom de conteneur source"
diff --git a/po/it.po b/po/it.po
index 15c5805fc..5d6371441 100644
--- a/po/it.po
+++ b/po/it.po
@@ -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: 2017-07-02 01:58-0400\n"
+"POT-Creation-Date: 2017-07-03 01:12-0400\n"
 "PO-Revision-Date: 2017-06-15 22:46+0000\n"
 "Last-Translator: Alberto Donato <alberto.donato at gmail.com>\n"
 "Language-Team: Italian <https://hosted.weblate.org/projects/linux-containers/"
@@ -279,7 +279,7 @@ msgstr ""
 msgid "Commands:"
 msgstr ""
 
-#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:136 lxc/init.go:137
+#: lxc/copy.go:34 lxc/copy.go:35 lxc/init.go:136 lxc/init.go:137
 msgid "Config key/value to apply to the new container"
 msgstr ""
 
@@ -297,7 +297,7 @@ msgstr ""
 msgid "Container name is mandatory"
 msgstr ""
 
-#: lxc/copy.go:220 lxc/init.go:314
+#: lxc/copy.go:224 lxc/init.go:314
 #, c-format
 msgid "Container name is: %s"
 msgstr ""
@@ -311,7 +311,7 @@ msgstr ""
 msgid "Copy aliases from source"
 msgstr ""
 
-#: lxc/copy.go:39
+#: lxc/copy.go:41
 msgid "Copy the container without its snapshots"
 msgstr ""
 
@@ -406,7 +406,7 @@ msgstr ""
 msgid "Environment:"
 msgstr ""
 
-#: lxc/copy.go:37 lxc/copy.go:38 lxc/init.go:140 lxc/init.go:141
+#: lxc/copy.go:38 lxc/copy.go:39 lxc/init.go:140 lxc/init.go:141
 msgid "Ephemeral container"
 msgstr ""
 
@@ -442,7 +442,7 @@ msgstr ""
 msgid "Failed to generate 'lxc.1': %v"
 msgstr ""
 
-#: lxc/copy.go:215
+#: lxc/copy.go:219
 msgid "Failed to get the new container name"
 msgstr ""
 
@@ -622,7 +622,7 @@ msgstr ""
 msgid "More than one file to download, but target is not a directory"
 msgstr ""
 
-#: lxc/move.go:38
+#: lxc/move.go:39
 msgid "Move the container without its snapshots"
 msgstr ""
 
@@ -800,7 +800,7 @@ msgstr ""
 msgid "Profile %s removed from %s"
 msgstr ""
 
-#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:138 lxc/init.go:139
+#: lxc/copy.go:36 lxc/copy.go:37 lxc/init.go:138 lxc/init.go:139
 msgid "Profile to apply to the new container"
 msgstr ""
 
@@ -1097,7 +1097,11 @@ msgstr ""
 msgid "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr ""
 
-#: lxc/copy.go:189
+#: lxc/copy.go:40 lxc/move.go:40
+msgid "Transfer mode. One of pull (default), push or relay."
+msgstr ""
+
+#: lxc/copy.go:193
 #, c-format
 msgid "Transfering container: %s"
 msgstr ""
@@ -1239,7 +1243,7 @@ msgid ""
 "    Will set the server's trust password to blah."
 msgstr ""
 
-#: lxc/copy.go:26
+#: lxc/copy.go:27
 msgid ""
 "Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] "
 "[--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...] [--"
@@ -1573,7 +1577,7 @@ msgid ""
 "    Only show log message."
 msgstr ""
 
-#: lxc/move.go:22
+#: lxc/move.go:23
 msgid ""
 "Usage: lxc move [<remote>:]<container>[/<snapshot>] [<remote>:][<container>[/"
 "<snapshot>]] [--container-only]\n"
@@ -1926,7 +1930,7 @@ msgstr ""
 msgid "You can't pass -t or -T at the same time as --mode"
 msgstr ""
 
-#: lxc/copy.go:57
+#: lxc/copy.go:59
 #, fuzzy
 msgid "You must specify a source container name"
 msgstr "occorre specificare un nome di container come origine"
diff --git a/po/ja.po b/po/ja.po
index 87ac6d6c1..a90bbf211 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -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: 2017-07-02 01:58-0400\n"
+"POT-Creation-Date: 2017-07-03 01:12-0400\n"
 "PO-Revision-Date: 2017-03-23 12:03+0000\n"
 "Last-Translator: KATOH Yasufumi <karma at jazz.email.ne.jp>\n"
 "Language-Team: Japanese <https://hosted.weblate.org/projects/linux-"
@@ -260,7 +260,7 @@ msgstr "カラムレイアウト"
 msgid "Commands:"
 msgstr "コマンド:"
 
-#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:136 lxc/init.go:137
+#: lxc/copy.go:34 lxc/copy.go:35 lxc/init.go:136 lxc/init.go:137
 msgid "Config key/value to apply to the new container"
 msgstr "新しいコンテナに適用するキー/値の設定"
 
@@ -278,7 +278,7 @@ msgstr "接続が拒否されました。LXDが実行されていますか?"
 msgid "Container name is mandatory"
 msgstr "コンテナ名を指定する必要があります"
 
-#: lxc/copy.go:220 lxc/init.go:314
+#: lxc/copy.go:224 lxc/init.go:314
 #, c-format
 msgid "Container name is: %s"
 msgstr "コンテナ名: %s"
@@ -292,7 +292,7 @@ msgstr "コンテナは以下のフィンガープリントで publish されま
 msgid "Copy aliases from source"
 msgstr "ソースからエイリアスをコピーしました"
 
-#: lxc/copy.go:39
+#: lxc/copy.go:41
 #, fuzzy
 msgid "Copy the container without its snapshots"
 msgstr "コンテナを強制シャットダウンします"
@@ -388,7 +388,7 @@ msgstr "環境変数を設定します (例: HOME=/home/foo)"
 msgid "Environment:"
 msgstr "環境変数:"
 
-#: lxc/copy.go:37 lxc/copy.go:38 lxc/init.go:140 lxc/init.go:141
+#: lxc/copy.go:38 lxc/copy.go:39 lxc/init.go:140 lxc/init.go:141
 msgid "Ephemeral container"
 msgstr "Ephemeral コンテナ"
 
@@ -424,7 +424,7 @@ msgstr "'lxc.%s.1' の生成が失敗しました: %v"
 msgid "Failed to generate 'lxc.1': %v"
 msgstr "'lxc.1' の生成が失敗しました: %v"
 
-#: lxc/copy.go:215
+#: lxc/copy.go:219
 #, fuzzy
 msgid "Failed to get the new container name"
 msgstr "新しいコンテナに適用するプロファイル"
@@ -610,7 +610,7 @@ msgstr ""
 "ダウンロード対象のファイルが複数ありますが、コピー先がディレクトリではありま"
 "せん"
 
-#: lxc/move.go:38
+#: lxc/move.go:39
 #, fuzzy
 msgid "Move the container without its snapshots"
 msgstr "コンテナを強制シャットダウンします"
@@ -789,7 +789,7 @@ msgstr "プロファイル %s を削除しました"
 msgid "Profile %s removed from %s"
 msgstr "プロファイル %s が %s から削除されました"
 
-#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:138 lxc/init.go:139
+#: lxc/copy.go:36 lxc/copy.go:37 lxc/init.go:138 lxc/init.go:139
 msgid "Profile to apply to the new container"
 msgstr "新しいコンテナに適用するプロファイル"
 
@@ -1100,7 +1100,11 @@ msgstr ""
 "初めてコンテナを起動するには、\"lxc launch ubuntu:16.04\" と実行してみてくだ"
 "さい"
 
-#: lxc/copy.go:189
+#: lxc/copy.go:40 lxc/move.go:40
+msgid "Transfer mode. One of pull (default), push or relay."
+msgstr ""
+
+#: lxc/copy.go:193
 #, fuzzy, c-format
 msgid "Transfering container: %s"
 msgstr "イメージを転送中: %s"
@@ -1307,7 +1311,7 @@ msgstr ""
 "lxc config set core.trust_password blah\n"
 "    サーバのパスワードを blah に設定する。"
 
-#: lxc/copy.go:26
+#: lxc/copy.go:27
 msgid ""
 "Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] "
 "[--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...] [--"
@@ -1896,7 +1900,7 @@ msgstr ""
 "lxc monitor --type=logging\n"
 "    ログメッセージのみ表示します。"
 
-#: lxc/move.go:22
+#: lxc/move.go:23
 msgid ""
 "Usage: lxc move [<remote>:]<container>[/<snapshot>] [<remote>:][<container>[/"
 "<snapshot>]] [--container-only]\n"
@@ -2535,7 +2539,7 @@ msgstr "-t と -T は同時に指定できません"
 msgid "You can't pass -t or -T at the same time as --mode"
 msgstr "--mode と同時に -t または -T は指定できません"
 
-#: lxc/copy.go:57
+#: lxc/copy.go:59
 #, fuzzy
 msgid "You must specify a source container name"
 msgstr "コピー元のコンテナ名を指定してください"
diff --git a/po/lxd.pot b/po/lxd.pot
index 00d40eb7b..ff1275036 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: 2017-07-02 01:58-0400\n"
+        "POT-Creation-Date: 2017-07-03 01:12-0400\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"
@@ -249,7 +249,7 @@ msgstr  ""
 msgid   "Commands:"
 msgstr  ""
 
-#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:136 lxc/init.go:137
+#: lxc/copy.go:34 lxc/copy.go:35 lxc/init.go:136 lxc/init.go:137
 msgid   "Config key/value to apply to the new container"
 msgstr  ""
 
@@ -266,7 +266,7 @@ msgstr  ""
 msgid   "Container name is mandatory"
 msgstr  ""
 
-#: lxc/copy.go:220 lxc/init.go:314
+#: lxc/copy.go:224 lxc/init.go:314
 #, c-format
 msgid   "Container name is: %s"
 msgstr  ""
@@ -280,7 +280,7 @@ msgstr  ""
 msgid   "Copy aliases from source"
 msgstr  ""
 
-#: lxc/copy.go:39
+#: lxc/copy.go:41
 msgid   "Copy the container without its snapshots"
 msgstr  ""
 
@@ -374,7 +374,7 @@ msgstr  ""
 msgid   "Environment:"
 msgstr  ""
 
-#: lxc/copy.go:37 lxc/copy.go:38 lxc/init.go:140 lxc/init.go:141
+#: lxc/copy.go:38 lxc/copy.go:39 lxc/init.go:140 lxc/init.go:141
 msgid   "Ephemeral container"
 msgstr  ""
 
@@ -410,7 +410,7 @@ msgstr  ""
 msgid   "Failed to generate 'lxc.1': %v"
 msgstr  ""
 
-#: lxc/copy.go:215
+#: lxc/copy.go:219
 msgid   "Failed to get the new container name"
 msgstr  ""
 
@@ -590,7 +590,7 @@ msgstr  ""
 msgid   "More than one file to download, but target is not a directory"
 msgstr  ""
 
-#: lxc/move.go:38
+#: lxc/move.go:39
 msgid   "Move the container without its snapshots"
 msgstr  ""
 
@@ -767,7 +767,7 @@ msgstr  ""
 msgid   "Profile %s removed from %s"
 msgstr  ""
 
-#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:138 lxc/init.go:139
+#: lxc/copy.go:36 lxc/copy.go:37 lxc/init.go:138 lxc/init.go:139
 msgid   "Profile to apply to the new container"
 msgstr  ""
 
@@ -1059,7 +1059,11 @@ msgstr  ""
 msgid   "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr  ""
 
-#: lxc/copy.go:189
+#: lxc/copy.go:40 lxc/move.go:40
+msgid   "Transfer mode. One of pull (default), push or relay."
+msgstr  ""
+
+#: lxc/copy.go:193
 #, c-format
 msgid   "Transfering container: %s"
 msgstr  ""
@@ -1197,7 +1201,7 @@ msgid   "Usage: lxc config <subcommand> [options]\n"
         "    Will set the server's trust password to blah."
 msgstr  ""
 
-#: lxc/copy.go:26
+#: lxc/copy.go:27
 msgid   "Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] [--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...] [--container-only]\n"
         "\n"
         "Copy containers within or in between LXD instances."
@@ -1490,7 +1494,7 @@ msgid   "Usage: lxc monitor [<remote>:] [--type=TYPE...]\n"
         "    Only show log message."
 msgstr  ""
 
-#: lxc/move.go:22
+#: lxc/move.go:23
 msgid   "Usage: lxc move [<remote>:]<container>[/<snapshot>] [<remote>:][<container>[/<snapshot>]] [--container-only]\n"
         "\n"
         "Move containers within or in between LXD instances.\n"
@@ -1808,7 +1812,7 @@ msgstr  ""
 msgid   "You can't pass -t or -T at the same time as --mode"
 msgstr  ""
 
-#: lxc/copy.go:57
+#: lxc/copy.go:59
 msgid   "You must specify a source container name"
 msgstr  ""
 
diff --git a/po/nl.po b/po/nl.po
index b82157709..2ab523c82 100644
--- a/po/nl.po
+++ b/po/nl.po
@@ -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: 2017-07-02 01:58-0400\n"
+"POT-Creation-Date: 2017-07-03 01:12-0400\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -255,7 +255,7 @@ msgstr ""
 msgid "Commands:"
 msgstr ""
 
-#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:136 lxc/init.go:137
+#: lxc/copy.go:34 lxc/copy.go:35 lxc/init.go:136 lxc/init.go:137
 msgid "Config key/value to apply to the new container"
 msgstr ""
 
@@ -273,7 +273,7 @@ msgstr ""
 msgid "Container name is mandatory"
 msgstr ""
 
-#: lxc/copy.go:220 lxc/init.go:314
+#: lxc/copy.go:224 lxc/init.go:314
 #, c-format
 msgid "Container name is: %s"
 msgstr ""
@@ -287,7 +287,7 @@ msgstr ""
 msgid "Copy aliases from source"
 msgstr ""
 
-#: lxc/copy.go:39
+#: lxc/copy.go:41
 msgid "Copy the container without its snapshots"
 msgstr ""
 
@@ -382,7 +382,7 @@ msgstr ""
 msgid "Environment:"
 msgstr ""
 
-#: lxc/copy.go:37 lxc/copy.go:38 lxc/init.go:140 lxc/init.go:141
+#: lxc/copy.go:38 lxc/copy.go:39 lxc/init.go:140 lxc/init.go:141
 msgid "Ephemeral container"
 msgstr ""
 
@@ -418,7 +418,7 @@ msgstr ""
 msgid "Failed to generate 'lxc.1': %v"
 msgstr ""
 
-#: lxc/copy.go:215
+#: lxc/copy.go:219
 msgid "Failed to get the new container name"
 msgstr ""
 
@@ -598,7 +598,7 @@ msgstr ""
 msgid "More than one file to download, but target is not a directory"
 msgstr ""
 
-#: lxc/move.go:38
+#: lxc/move.go:39
 msgid "Move the container without its snapshots"
 msgstr ""
 
@@ -776,7 +776,7 @@ msgstr ""
 msgid "Profile %s removed from %s"
 msgstr ""
 
-#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:138 lxc/init.go:139
+#: lxc/copy.go:36 lxc/copy.go:37 lxc/init.go:138 lxc/init.go:139
 msgid "Profile to apply to the new container"
 msgstr ""
 
@@ -1072,7 +1072,11 @@ msgstr ""
 msgid "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr ""
 
-#: lxc/copy.go:189
+#: lxc/copy.go:40 lxc/move.go:40
+msgid "Transfer mode. One of pull (default), push or relay."
+msgstr ""
+
+#: lxc/copy.go:193
 #, c-format
 msgid "Transfering container: %s"
 msgstr ""
@@ -1214,7 +1218,7 @@ msgid ""
 "    Will set the server's trust password to blah."
 msgstr ""
 
-#: lxc/copy.go:26
+#: lxc/copy.go:27
 msgid ""
 "Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] "
 "[--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...] [--"
@@ -1548,7 +1552,7 @@ msgid ""
 "    Only show log message."
 msgstr ""
 
-#: lxc/move.go:22
+#: lxc/move.go:23
 msgid ""
 "Usage: lxc move [<remote>:]<container>[/<snapshot>] [<remote>:][<container>[/"
 "<snapshot>]] [--container-only]\n"
@@ -1901,7 +1905,7 @@ msgstr ""
 msgid "You can't pass -t or -T at the same time as --mode"
 msgstr ""
 
-#: lxc/copy.go:57
+#: lxc/copy.go:59
 msgid "You must specify a source container name"
 msgstr ""
 
diff --git a/po/ru.po b/po/ru.po
index 32f32062c..bcd2eebc7 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -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: 2017-07-02 01:58-0400\n"
+"POT-Creation-Date: 2017-07-03 01:12-0400\n"
 "PO-Revision-Date: 2017-06-06 13:55+0000\n"
 "Last-Translator: Александр Киль <shorrey at gmail.com>\n"
 "Language-Team: Russian <https://hosted.weblate.org/projects/linux-containers/"
@@ -328,7 +328,7 @@ msgstr "Столбцы"
 msgid "Commands:"
 msgstr ""
 
-#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:136 lxc/init.go:137
+#: lxc/copy.go:34 lxc/copy.go:35 lxc/init.go:136 lxc/init.go:137
 msgid "Config key/value to apply to the new container"
 msgstr ""
 
@@ -346,7 +346,7 @@ msgstr "В соединении отказано; LXD запущен?"
 msgid "Container name is mandatory"
 msgstr "Имя контейнера является обязательным"
 
-#: lxc/copy.go:220 lxc/init.go:314
+#: lxc/copy.go:224 lxc/init.go:314
 #, c-format
 msgid "Container name is: %s"
 msgstr "Имя контейнера: %s"
@@ -360,7 +360,7 @@ msgstr ""
 msgid "Copy aliases from source"
 msgstr "Копировать псевдонимы из источника"
 
-#: lxc/copy.go:39
+#: lxc/copy.go:41
 msgid "Copy the container without its snapshots"
 msgstr ""
 
@@ -456,7 +456,7 @@ msgstr ""
 msgid "Environment:"
 msgstr ""
 
-#: lxc/copy.go:37 lxc/copy.go:38 lxc/init.go:140 lxc/init.go:141
+#: lxc/copy.go:38 lxc/copy.go:39 lxc/init.go:140 lxc/init.go:141
 msgid "Ephemeral container"
 msgstr ""
 
@@ -492,7 +492,7 @@ msgstr ""
 msgid "Failed to generate 'lxc.1': %v"
 msgstr ""
 
-#: lxc/copy.go:215
+#: lxc/copy.go:219
 msgid "Failed to get the new container name"
 msgstr ""
 
@@ -673,7 +673,7 @@ msgstr ""
 msgid "More than one file to download, but target is not a directory"
 msgstr ""
 
-#: lxc/move.go:38
+#: lxc/move.go:39
 msgid "Move the container without its snapshots"
 msgstr ""
 
@@ -852,7 +852,7 @@ msgstr ""
 msgid "Profile %s removed from %s"
 msgstr ""
 
-#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:138 lxc/init.go:139
+#: lxc/copy.go:36 lxc/copy.go:37 lxc/init.go:138 lxc/init.go:139
 msgid "Profile to apply to the new container"
 msgstr ""
 
@@ -1148,7 +1148,11 @@ msgstr ""
 msgid "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr ""
 
-#: lxc/copy.go:189
+#: lxc/copy.go:40 lxc/move.go:40
+msgid "Transfer mode. One of pull (default), push or relay."
+msgstr ""
+
+#: lxc/copy.go:193
 #, c-format
 msgid "Transfering container: %s"
 msgstr ""
@@ -1293,7 +1297,7 @@ msgid ""
 "    Will set the server's trust password to blah."
 msgstr ""
 
-#: lxc/copy.go:26
+#: lxc/copy.go:27
 #, fuzzy
 msgid ""
 "Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] "
@@ -1632,7 +1636,7 @@ msgid ""
 "    Only show log message."
 msgstr ""
 
-#: lxc/move.go:22
+#: lxc/move.go:23
 msgid ""
 "Usage: lxc move [<remote>:]<container>[/<snapshot>] [<remote>:][<container>[/"
 "<snapshot>]] [--container-only]\n"
@@ -1985,7 +1989,7 @@ msgstr ""
 msgid "You can't pass -t or -T at the same time as --mode"
 msgstr ""
 
-#: lxc/copy.go:57
+#: lxc/copy.go:59
 msgid "You must specify a source container name"
 msgstr ""
 
diff --git a/po/sr.po b/po/sr.po
index dea2fe71d..8ea7c9d44 100644
--- a/po/sr.po
+++ b/po/sr.po
@@ -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: 2017-07-02 01:58-0400\n"
+"POT-Creation-Date: 2017-07-03 01:12-0400\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -255,7 +255,7 @@ msgstr ""
 msgid "Commands:"
 msgstr ""
 
-#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:136 lxc/init.go:137
+#: lxc/copy.go:34 lxc/copy.go:35 lxc/init.go:136 lxc/init.go:137
 msgid "Config key/value to apply to the new container"
 msgstr ""
 
@@ -273,7 +273,7 @@ msgstr ""
 msgid "Container name is mandatory"
 msgstr ""
 
-#: lxc/copy.go:220 lxc/init.go:314
+#: lxc/copy.go:224 lxc/init.go:314
 #, c-format
 msgid "Container name is: %s"
 msgstr ""
@@ -287,7 +287,7 @@ msgstr ""
 msgid "Copy aliases from source"
 msgstr ""
 
-#: lxc/copy.go:39
+#: lxc/copy.go:41
 msgid "Copy the container without its snapshots"
 msgstr ""
 
@@ -382,7 +382,7 @@ msgstr ""
 msgid "Environment:"
 msgstr ""
 
-#: lxc/copy.go:37 lxc/copy.go:38 lxc/init.go:140 lxc/init.go:141
+#: lxc/copy.go:38 lxc/copy.go:39 lxc/init.go:140 lxc/init.go:141
 msgid "Ephemeral container"
 msgstr ""
 
@@ -418,7 +418,7 @@ msgstr ""
 msgid "Failed to generate 'lxc.1': %v"
 msgstr ""
 
-#: lxc/copy.go:215
+#: lxc/copy.go:219
 msgid "Failed to get the new container name"
 msgstr ""
 
@@ -598,7 +598,7 @@ msgstr ""
 msgid "More than one file to download, but target is not a directory"
 msgstr ""
 
-#: lxc/move.go:38
+#: lxc/move.go:39
 msgid "Move the container without its snapshots"
 msgstr ""
 
@@ -776,7 +776,7 @@ msgstr ""
 msgid "Profile %s removed from %s"
 msgstr ""
 
-#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:138 lxc/init.go:139
+#: lxc/copy.go:36 lxc/copy.go:37 lxc/init.go:138 lxc/init.go:139
 msgid "Profile to apply to the new container"
 msgstr ""
 
@@ -1072,7 +1072,11 @@ msgstr ""
 msgid "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr ""
 
-#: lxc/copy.go:189
+#: lxc/copy.go:40 lxc/move.go:40
+msgid "Transfer mode. One of pull (default), push or relay."
+msgstr ""
+
+#: lxc/copy.go:193
 #, c-format
 msgid "Transfering container: %s"
 msgstr ""
@@ -1214,7 +1218,7 @@ msgid ""
 "    Will set the server's trust password to blah."
 msgstr ""
 
-#: lxc/copy.go:26
+#: lxc/copy.go:27
 msgid ""
 "Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] "
 "[--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...] [--"
@@ -1548,7 +1552,7 @@ msgid ""
 "    Only show log message."
 msgstr ""
 
-#: lxc/move.go:22
+#: lxc/move.go:23
 msgid ""
 "Usage: lxc move [<remote>:]<container>[/<snapshot>] [<remote>:][<container>[/"
 "<snapshot>]] [--container-only]\n"
@@ -1901,7 +1905,7 @@ msgstr ""
 msgid "You can't pass -t or -T at the same time as --mode"
 msgstr ""
 
-#: lxc/copy.go:57
+#: lxc/copy.go:59
 msgid "You must specify a source container name"
 msgstr ""
 
diff --git a/po/sv.po b/po/sv.po
index c7f4aaf3e..ded933497 100644
--- a/po/sv.po
+++ b/po/sv.po
@@ -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: 2017-07-02 01:58-0400\n"
+"POT-Creation-Date: 2017-07-03 01:12-0400\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -255,7 +255,7 @@ msgstr ""
 msgid "Commands:"
 msgstr ""
 
-#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:136 lxc/init.go:137
+#: lxc/copy.go:34 lxc/copy.go:35 lxc/init.go:136 lxc/init.go:137
 msgid "Config key/value to apply to the new container"
 msgstr ""
 
@@ -273,7 +273,7 @@ msgstr ""
 msgid "Container name is mandatory"
 msgstr ""
 
-#: lxc/copy.go:220 lxc/init.go:314
+#: lxc/copy.go:224 lxc/init.go:314
 #, c-format
 msgid "Container name is: %s"
 msgstr ""
@@ -287,7 +287,7 @@ msgstr ""
 msgid "Copy aliases from source"
 msgstr ""
 
-#: lxc/copy.go:39
+#: lxc/copy.go:41
 msgid "Copy the container without its snapshots"
 msgstr ""
 
@@ -382,7 +382,7 @@ msgstr ""
 msgid "Environment:"
 msgstr ""
 
-#: lxc/copy.go:37 lxc/copy.go:38 lxc/init.go:140 lxc/init.go:141
+#: lxc/copy.go:38 lxc/copy.go:39 lxc/init.go:140 lxc/init.go:141
 msgid "Ephemeral container"
 msgstr ""
 
@@ -418,7 +418,7 @@ msgstr ""
 msgid "Failed to generate 'lxc.1': %v"
 msgstr ""
 
-#: lxc/copy.go:215
+#: lxc/copy.go:219
 msgid "Failed to get the new container name"
 msgstr ""
 
@@ -598,7 +598,7 @@ msgstr ""
 msgid "More than one file to download, but target is not a directory"
 msgstr ""
 
-#: lxc/move.go:38
+#: lxc/move.go:39
 msgid "Move the container without its snapshots"
 msgstr ""
 
@@ -776,7 +776,7 @@ msgstr ""
 msgid "Profile %s removed from %s"
 msgstr ""
 
-#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:138 lxc/init.go:139
+#: lxc/copy.go:36 lxc/copy.go:37 lxc/init.go:138 lxc/init.go:139
 msgid "Profile to apply to the new container"
 msgstr ""
 
@@ -1072,7 +1072,11 @@ msgstr ""
 msgid "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr ""
 
-#: lxc/copy.go:189
+#: lxc/copy.go:40 lxc/move.go:40
+msgid "Transfer mode. One of pull (default), push or relay."
+msgstr ""
+
+#: lxc/copy.go:193
 #, c-format
 msgid "Transfering container: %s"
 msgstr ""
@@ -1214,7 +1218,7 @@ msgid ""
 "    Will set the server's trust password to blah."
 msgstr ""
 
-#: lxc/copy.go:26
+#: lxc/copy.go:27
 msgid ""
 "Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] "
 "[--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...] [--"
@@ -1548,7 +1552,7 @@ msgid ""
 "    Only show log message."
 msgstr ""
 
-#: lxc/move.go:22
+#: lxc/move.go:23
 msgid ""
 "Usage: lxc move [<remote>:]<container>[/<snapshot>] [<remote>:][<container>[/"
 "<snapshot>]] [--container-only]\n"
@@ -1901,7 +1905,7 @@ msgstr ""
 msgid "You can't pass -t or -T at the same time as --mode"
 msgstr ""
 
-#: lxc/copy.go:57
+#: lxc/copy.go:59
 msgid "You must specify a source container name"
 msgstr ""
 
diff --git a/po/tr.po b/po/tr.po
index c5d085ac0..8daa56623 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -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: 2017-07-02 01:58-0400\n"
+"POT-Creation-Date: 2017-07-03 01:12-0400\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -255,7 +255,7 @@ msgstr ""
 msgid "Commands:"
 msgstr ""
 
-#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:136 lxc/init.go:137
+#: lxc/copy.go:34 lxc/copy.go:35 lxc/init.go:136 lxc/init.go:137
 msgid "Config key/value to apply to the new container"
 msgstr ""
 
@@ -273,7 +273,7 @@ msgstr ""
 msgid "Container name is mandatory"
 msgstr ""
 
-#: lxc/copy.go:220 lxc/init.go:314
+#: lxc/copy.go:224 lxc/init.go:314
 #, c-format
 msgid "Container name is: %s"
 msgstr ""
@@ -287,7 +287,7 @@ msgstr ""
 msgid "Copy aliases from source"
 msgstr ""
 
-#: lxc/copy.go:39
+#: lxc/copy.go:41
 msgid "Copy the container without its snapshots"
 msgstr ""
 
@@ -382,7 +382,7 @@ msgstr ""
 msgid "Environment:"
 msgstr ""
 
-#: lxc/copy.go:37 lxc/copy.go:38 lxc/init.go:140 lxc/init.go:141
+#: lxc/copy.go:38 lxc/copy.go:39 lxc/init.go:140 lxc/init.go:141
 msgid "Ephemeral container"
 msgstr ""
 
@@ -418,7 +418,7 @@ msgstr ""
 msgid "Failed to generate 'lxc.1': %v"
 msgstr ""
 
-#: lxc/copy.go:215
+#: lxc/copy.go:219
 msgid "Failed to get the new container name"
 msgstr ""
 
@@ -598,7 +598,7 @@ msgstr ""
 msgid "More than one file to download, but target is not a directory"
 msgstr ""
 
-#: lxc/move.go:38
+#: lxc/move.go:39
 msgid "Move the container without its snapshots"
 msgstr ""
 
@@ -776,7 +776,7 @@ msgstr ""
 msgid "Profile %s removed from %s"
 msgstr ""
 
-#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:138 lxc/init.go:139
+#: lxc/copy.go:36 lxc/copy.go:37 lxc/init.go:138 lxc/init.go:139
 msgid "Profile to apply to the new container"
 msgstr ""
 
@@ -1072,7 +1072,11 @@ msgstr ""
 msgid "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr ""
 
-#: lxc/copy.go:189
+#: lxc/copy.go:40 lxc/move.go:40
+msgid "Transfer mode. One of pull (default), push or relay."
+msgstr ""
+
+#: lxc/copy.go:193
 #, c-format
 msgid "Transfering container: %s"
 msgstr ""
@@ -1214,7 +1218,7 @@ msgid ""
 "    Will set the server's trust password to blah."
 msgstr ""
 
-#: lxc/copy.go:26
+#: lxc/copy.go:27
 msgid ""
 "Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] "
 "[--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...] [--"
@@ -1548,7 +1552,7 @@ msgid ""
 "    Only show log message."
 msgstr ""
 
-#: lxc/move.go:22
+#: lxc/move.go:23
 msgid ""
 "Usage: lxc move [<remote>:]<container>[/<snapshot>] [<remote>:][<container>[/"
 "<snapshot>]] [--container-only]\n"
@@ -1901,7 +1905,7 @@ msgstr ""
 msgid "You can't pass -t or -T at the same time as --mode"
 msgstr ""
 
-#: lxc/copy.go:57
+#: lxc/copy.go:59
 msgid "You must specify a source container name"
 msgstr ""
 


More information about the lxc-devel mailing list