func (r *ProtocolLXD) CreateContainerSnapshot(containerName string, snapshot api.ContainerSnapshotsPost) (Operation, error) {
	op, _, err := r.queryOperation("POST", fmt.Sprintf("/containers/%s/snapshots", url.QueryEscape(containerName)), snapshot, "")
	if err != nil {
		return nil, err
	}
	return op, nil
}
CreateContainerSnapshot requests that LXD creates a new snapshot for the container
package lxd
import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"github.com/gorilla/websocket"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/cancel"
	"github.com/lxc/lxd/shared/ioprogress"
)
func (r *ProtocolLXD) GetContainerNames() ([]string, error) {
	urls := []string{}
	_, err := r.queryStruct("GET", "/containers", nil, "", &urls)
	if err != nil {
		return nil, err
	}
	names := []string{}
	for _, url := range urls {
		fields := strings.Split(url, "/containers/")
		names = append(names, fields[len(fields)-1])
	}
	return names, nil
}
func (r *ProtocolLXD) GetContainers() ([]api.Container, error) {
	containers := []api.Container{}
	_, err := r.queryStruct("GET", "/containers?recursion=1", nil, "", &containers)
	if err != nil {
		return nil, err
	}
	return containers, nil
}
func (r *ProtocolLXD) GetContainer(name string) (*api.Container, string, error) {
	container := api.Container{}
	etag, err := r.queryStruct("GET", fmt.Sprintf("/containers/%s", url.QueryEscape(name)), nil, "", &container)
	if err != nil {
		return nil, "", err
	}
	return &container, etag, nil
}
func (r *ProtocolLXD) CreateContainerFromBackup(args ContainerBackupArgs) (Operation, error) {
	if !r.HasExtension("container_backup") {
		return nil, fmt.Errorf("The server is missing the required \"container_backup\" API extension")
	}
	path := "/containers"
	if r.clusterTarget != "" {
		path += fmt.Sprintf("?target=%s", r.clusterTarget)
	}
	op, _, err := r.queryOperation("POST", path, args.BackupFile, "")
	if err != nil {
		return nil, err
	}
	return op, nil
}
func (r *ProtocolLXD) CreateContainer(container api.ContainersPost) (Operation, error) {
	if container.Source.ContainerOnly {
		if !r.HasExtension("container_only_migration") {
			return nil, fmt.Errorf("The server is missing the required \"container_only_migration\" API extension")
		}
	}
	path := "/containers"
	if r.clusterTarget != "" {
		path += fmt.Sprintf("?target=%s", r.clusterTarget)
	}
	op, _, err := r.queryOperation("POST", path, container, "")
	if err != nil {
		return nil, err
	}
	return op, nil
}
func (r *ProtocolLXD) tryCreateContainer(req api.ContainersPost, urls []string) (RemoteOperation, error) {
	if len(urls) == 0 {
		return nil, fmt.Errorf("The source server isn't listening on the network")
	}
	rop := remoteOperation{
		chDone: make(chan bool),
	}
	operation := req.Source.Operation
	go func() {
		success := false
		errors := map[string]error{}
		for _, serverURL := range urls {
			if operation == "" {
				req.Source.Server = serverURL
			} else {
				req.Source.Operation = fmt.Sprintf("%s/1.0/operations/%s", serverURL, url.QueryEscape(operation))
			}
			op, err := r.CreateContainer(req)
			if err != nil {
				errors[serverURL] = err
				continue
			}
			rop.targetOp = op
			for _, handler := range rop.handlers {
				rop.targetOp.AddHandler(handler)
			}
			err = rop.targetOp.Wait()
			if err != nil {
				errors[serverURL] = err
				continue
			}
			success = true
			break
		}
		if !success {
			rop.err = remoteOperationError("Failed container creation", errors)
		}
		close(rop.chDone)
	}()
	return &rop, nil
}
func (r *ProtocolLXD) CreateContainerFromImage(source ImageServer, image api.Image, req api.ContainersPost) (RemoteOperation, error) {
	req.Source.Type = "image"
	if r == source {
		req.Source.Fingerprint = image.Fingerprint
		req.Source.Alias = ""
		op, err := r.CreateContainer(req)
		if err != nil {
			return nil, err
		}
		rop := remoteOperation{
			targetOp: op,
			chDone:   make(chan bool),
		}
		go func() {
			rop.err = rop.targetOp.Wait()
			close(rop.chDone)
		}()
		return &rop, nil
	}
	req.Source.Mode = "pull"
	if req.Source.Alias != "" && image.Public {
		req.Source.Fingerprint = ""
	} else {
		req.Source.Fingerprint = image.Fingerprint
		req.Source.Alias = ""
	}
	info, err := source.GetConnectionInfo()
	if err != nil {
		return nil, err
	}
	req.Source.Protocol = info.Protocol
	req.Source.Certificate = info.Certificate
	if !image.Public {
		secret, err := source.GetImageSecret(image.Fingerprint)
		if err != nil {
			return nil, err
		}
		req.Source.Secret = secret
	}
	return r.tryCreateContainer(req, info.Addresses)
}
func (r *ProtocolLXD) CopyContainer(source ContainerServer, container api.Container, args *ContainerCopyArgs) (RemoteOperation, error) {
	req := api.ContainersPost{
		Name:         container.Name,
		ContainerPut: container.Writable(),
	}
	req.Source.BaseImage = container.Config["volatile.base_image"]
	if args != nil {
		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")
		}
		if args.Name != "" {
			req.Name = args.Name
		}
		req.Source.Live = args.Live
		req.Source.ContainerOnly = args.ContainerOnly
	}
	if req.Source.Live {
		req.Source.Live = container.StatusCode == api.Running
	}
	if r == source && !r.IsClustered() {
		req.Source.Type = "copy"
		req.Source.Source = container.Name
		op, err := r.CreateContainer(req)
		if err != nil {
			return nil, err
		}
		rop := remoteOperation{
			targetOp: op,
			chDone:   make(chan bool),
		}
		go func() {
			rop.err = rop.targetOp.Wait()
			close(rop.chDone)
		}()
		return &rop, nil
	}
	sourceReq := api.ContainerPost{
		Migration:     true,
		Live:          req.Source.Live,
		ContainerOnly: req.Source.ContainerOnly,
	}
	if args != nil && args.Mode == "push" {
		info, err := r.GetConnectionInfo()
		if err != nil {
			return nil, err
		}
		req.Source.Type = "migration"
		req.Source.Mode = "push"
		op, err := r.CreateContainer(req)
		if err != nil {
			return nil, err
		}
		opAPI := op.Get()
		targetSecrets := map[string]string{}
		for k, v := range opAPI.Metadata {
			targetSecrets[k] = v.(string)
		}
		target := api.ContainerPostTarget{}
		target.Operation = opAPI.ID
		target.Websockets = targetSecrets
		target.Certificate = info.Certificate
		sourceReq.Target = &target
		return r.tryMigrateContainer(source, container.Name, sourceReq, info.Addresses)
	}
	info, err := source.GetConnectionInfo()
	if err != nil {
		return nil, err
	}
	op, err := source.MigrateContainer(container.Name, sourceReq)
	if err != nil {
		return nil, err
	}
	opAPI := op.Get()
	sourceSecrets := map[string]string{}
	for k, v := range opAPI.Metadata {
		sourceSecrets[k] = v.(string)
	}
	if args != nil && args.Mode == "relay" {
		req.Source.Type = "migration"
		req.Source.Mode = "push"
		targetOp, err := r.CreateContainer(req)
		if err != nil {
			return nil, err
		}
		targetOpAPI := targetOp.Get()
		targetSecrets := map[string]string{}
		for k, v := range targetOpAPI.Metadata {
			targetSecrets[k] = v.(string)
		}
		err = r.proxyMigration(targetOp.(*operation), targetSecrets, source, op.(*operation), sourceSecrets)
		if err != nil {
			return nil, err
		}
		rop := remoteOperation{
			targetOp: targetOp,
			chDone:   make(chan bool),
		}
		go func() {
			rop.err = rop.targetOp.Wait()
			close(rop.chDone)
		}()
		return &rop, nil
	}
	req.Source.Type = "migration"
	req.Source.Mode = "pull"
	req.Source.Operation = opAPI.ID
	req.Source.Websockets = sourceSecrets
	req.Source.Certificate = info.Certificate
	return r.tryCreateContainer(req, info.Addresses)
}
func (r *ProtocolLXD) proxyMigration(targetOp *operation, targetSecrets map[string]string, source ContainerServer, sourceOp *operation, sourceSecrets map[string]string) error {
	for n := range targetSecrets {
		_, ok := sourceSecrets[n]
		if !ok {
			return fmt.Errorf("Migration target expects the \"%s\" socket but source isn't providing it", n)
		}
	}
	if targetSecrets["control"] == "" {
		return fmt.Errorf("Migration target didn't setup the required \"control\" socket")
	}
	type proxy struct {
		done       chan bool
		sourceConn *websocket.Conn
		targetConn *websocket.Conn
	}
	proxies := map[string]*proxy{}
	sourceConn, err := source.GetOperationWebsocket(sourceOp.ID, sourceSecrets["control"])
	if err != nil {
		return err
	}
	targetConn, err := r.GetOperationWebsocket(targetOp.ID, targetSecrets["control"])
	if err != nil {
		return err
	}
	proxies["control"] = &proxy{
		done:       shared.WebsocketProxy(sourceConn, targetConn),
		sourceConn: sourceConn,
		targetConn: targetConn,
	}
	for name := range sourceSecrets {
		if name == "control" {
			continue
		}
		sourceConn, err := source.GetOperationWebsocket(sourceOp.ID, sourceSecrets[name])
		if err != nil {
			break
		}
		targetConn, err := r.GetOperationWebsocket(targetOp.ID, targetSecrets[name])
		if err != nil {
			break
		}
		proxies[name] = &proxy{
			sourceConn: sourceConn,
			targetConn: targetConn,
			done:       shared.WebsocketProxy(sourceConn, targetConn),
		}
	}
	go func() {
		<-proxies["control"].done
		proxies["control"].sourceConn.Close()
		proxies["control"].targetConn.Close()
		for name, proxy := range proxies {
			if name == "control" {
				continue
			}
			<-proxy.done
			proxy.sourceConn.Close()
			proxy.targetConn.Close()
		}
	}()
	return nil
}
func (r *ProtocolLXD) UpdateContainer(name string, container api.ContainerPut, ETag string) (Operation, error) {
	op, _, err := r.queryOperation("PUT", fmt.Sprintf("/containers/%s", url.QueryEscape(name)), container, ETag)
	if err != nil {
		return nil, err
	}
	return op, nil
}
func (r *ProtocolLXD) RenameContainer(name string, container api.ContainerPost) (Operation, error) {
	if container.Migration {
		return nil, fmt.Errorf("Can't ask for a migration through RenameContainer")
	}
	op, _, err := r.queryOperation("POST", fmt.Sprintf("/containers/%s", url.QueryEscape(name)), container, "")
	if err != nil {
		return nil, err
	}
	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
	go func() {
		success := false
		errors := map[string]error{}
		for _, serverURL := range urls {
			req.Target.Operation = fmt.Sprintf("%s/1.0/operations/%s", serverURL, url.QueryEscape(operation))
			op, err := source.MigrateContainer(name, req)
			if err != nil {
				errors[serverURL] = err
				continue
			}
			rop.targetOp = op
			for _, handler := range rop.handlers {
				rop.targetOp.AddHandler(handler)
			}
			err = rop.targetOp.Wait()
			if err != nil {
				errors[serverURL] = err
				continue
			}
			success = true
			break
		}
		if !success {
			rop.err = remoteOperationError("Failed container migration", errors)
		}
		close(rop.chDone)
	}()
	return &rop, nil
}
func (r *ProtocolLXD) MigrateContainer(name string, container api.ContainerPost) (Operation, error) {
	if container.ContainerOnly {
		if !r.HasExtension("container_only_migration") {
			return nil, fmt.Errorf("The server is missing the required \"container_only_migration\" API extension")
		}
	}
	if !container.Migration {
		return nil, fmt.Errorf("Can't ask for a rename through MigrateContainer")
	}
	path := fmt.Sprintf("/containers/%s", url.QueryEscape(name))
	if r.clusterTarget != "" {
		path += fmt.Sprintf("?target=%s", r.clusterTarget)
	}
	op, _, err := r.queryOperation("POST", path, container, "")
	if err != nil {
		return nil, err
	}
	return op, nil
}
func (r *ProtocolLXD) DeleteContainer(name string) (Operation, error) {
	op, _, err := r.queryOperation("DELETE", fmt.Sprintf("/containers/%s", url.QueryEscape(name)), nil, "")
	if err != nil {
		return nil, err
	}
	return op, nil
}
func (r *ProtocolLXD) ExecContainer(containerName string, exec api.ContainerExecPost, args *ContainerExecArgs) (Operation, error) {
	if exec.RecordOutput {
		if !r.HasExtension("container_exec_recording") {
			return nil, fmt.Errorf("The server is missing the required \"container_exec_recording\" API extension")
		}
	}
	op, _, err := r.queryOperation("POST", fmt.Sprintf("/containers/%s/exec", url.QueryEscape(containerName)), exec, "")
	if err != nil {
		return nil, err
	}
	opAPI := op.Get()
	if args != nil {
		fds := map[string]string{}
		value, ok := opAPI.Metadata["fds"]
		if ok {
			values := value.(map[string]interface{})
			for k, v := range values {
				fds[k] = v.(string)
			}
		}
		if args.Control != nil && fds["control"] != "" {
			conn, err := r.GetOperationWebsocket(opAPI.ID, fds["control"])
			if err != nil {
				return nil, err
			}
			go args.Control(conn)
		}
		if exec.Interactive {
			if args.Stdin != nil && args.Stdout != nil {
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["0"])
				if err != nil {
					return nil, err
				}
				go func() {
					shared.WebsocketSendStream(conn, args.Stdin, -1)
					<-shared.WebsocketRecvStream(args.Stdout, conn)
					conn.Close()
					if args.DataDone != nil {
						close(args.DataDone)
					}
				}()
			} else {
				if args.DataDone != nil {
					close(args.DataDone)
				}
			}
		} else {
			dones := map[int]chan bool{}
			conns := []*websocket.Conn{}
			if fds["0"] != "" {
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["0"])
				if err != nil {
					return nil, err
				}
				conns = append(conns, conn)
				dones[0] = shared.WebsocketSendStream(conn, args.Stdin, -1)
			}
			if fds["1"] != "" {
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["1"])
				if err != nil {
					return nil, err
				}
				conns = append(conns, conn)
				dones[1] = shared.WebsocketRecvStream(args.Stdout, conn)
			}
			if fds["2"] != "" {
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["2"])
				if err != nil {
					return nil, err
				}
				conns = append(conns, conn)
				dones[2] = shared.WebsocketRecvStream(args.Stderr, conn)
			}
			go func() {
				for i, chDone := range dones {
					if i == 0 {
						continue
					}
					<-chDone
				}
				if fds["0"] != "" {
					args.Stdin.Close()
				}
				for _, conn := range conns {
					conn.Close()
				}
				if args.DataDone != nil {
					close(args.DataDone)
				}
			}()
		}
	}
	return op, nil
}
func (r *ProtocolLXD) GetContainerFile(containerName string, path string) (io.ReadCloser, *ContainerFileResponse, error) {
	requestURL, err := shared.URLEncode(
		fmt.Sprintf("%s/1.0/containers/%s/files", r.httpHost, url.QueryEscape(containerName)),
		map[string]string{"path": path})
	if err != nil {
		return nil, nil, err
	}
	req, err := http.NewRequest("GET", requestURL, nil)
	if err != nil {
		return nil, nil, err
	}
	if r.httpUserAgent != "" {
		req.Header.Set("User-Agent", r.httpUserAgent)
	}
	resp, err := r.do(req)
	if err != nil {
		return nil, nil, err
	}
	if resp.StatusCode != http.StatusOK {
		_, _, err := r.parseResponse(resp)
		if err != nil {
			return nil, nil, err
		}
	}
	uid, gid, mode, fileType, _ := shared.ParseLXDFileHeaders(resp.Header)
	fileResp := ContainerFileResponse{
		UID:  uid,
		GID:  gid,
		Mode: mode,
		Type: fileType,
	}
	if fileResp.Type == "directory" {
		response := api.Response{}
		decoder := json.NewDecoder(resp.Body)
		err = decoder.Decode(&response)
		if err != nil {
			return nil, nil, err
		}
		entries := []string{}
		err = response.MetadataAsStruct(&entries)
		if err != nil {
			return nil, nil, err
		}
		fileResp.Entries = entries
		return nil, &fileResp, err
	}
	return resp.Body, &fileResp, err
}
func (r *ProtocolLXD) CreateContainerFile(containerName string, path string, args ContainerFileArgs) error {
	if args.Type == "directory" {
		if !r.HasExtension("directory_manipulation") {
			return fmt.Errorf("The server is missing the required \"directory_manipulation\" API extension")
		}
	}
	if args.Type == "symlink" {
		if !r.HasExtension("file_symlinks") {
			return fmt.Errorf("The server is missing the required \"file_symlinks\" API extension")
		}
	}
	if args.WriteMode == "append" {
		if !r.HasExtension("file_append") {
			return fmt.Errorf("The server is missing the required \"file_append\" API extension")
		}
	}
	req, err := http.NewRequest("POST", fmt.Sprintf("%s/1.0/containers/%s/files?path=%s", r.httpHost, url.QueryEscape(containerName), url.QueryEscape(path)), args.Content)
	if err != nil {
		return err
	}
	if r.httpUserAgent != "" {
		req.Header.Set("User-Agent", r.httpUserAgent)
	}
	if args.UID > -1 {
		req.Header.Set("X-LXD-uid", fmt.Sprintf("%d", args.UID))
	}
	if args.GID > -1 {
		req.Header.Set("X-LXD-gid", fmt.Sprintf("%d", args.GID))
	}
	if args.Mode > -1 {
		req.Header.Set("X-LXD-mode", fmt.Sprintf("%04o", args.Mode))
	}
	if args.Type != "" {
		req.Header.Set("X-LXD-type", args.Type)
	}
	if args.WriteMode != "" {
		req.Header.Set("X-LXD-write", args.WriteMode)
	}
	resp, err := r.do(req)
	if err != nil {
		return err
	}
	_, _, err = r.parseResponse(resp)
	if err != nil {
		return err
	}
	return nil
}
func (r *ProtocolLXD) DeleteContainerFile(containerName string, path string) error {
	if !r.HasExtension("file_delete") {
		return fmt.Errorf("The server is missing the required \"file_delete\" API extension")
	}
	_, _, err := r.query("DELETE", fmt.Sprintf("/containers/%s/files?path=%s", url.QueryEscape(containerName), url.QueryEscape(path)), nil, "")
	if err != nil {
		return err
	}
	return nil
}
func (r *ProtocolLXD) GetContainerSnapshotNames(containerName string) ([]string, error) {
	urls := []string{}
	_, err := r.queryStruct("GET", fmt.Sprintf("/containers/%s/snapshots", url.QueryEscape(containerName)), nil, "", &urls)
	if err != nil {
		return nil, err
	}
	names := []string{}
	for _, uri := range urls {
		fields := strings.Split(uri, fmt.Sprintf("/containers/%s/snapshots/", url.QueryEscape(containerName)))
		names = append(names, fields[len(fields)-1])
	}
	return names, nil
}
func (r *ProtocolLXD) GetContainerSnapshots(containerName string) ([]api.ContainerSnapshot, error) {
	snapshots := []api.ContainerSnapshot{}
	_, err := r.queryStruct("GET", fmt.Sprintf("/containers/%s/snapshots?recursion=1", url.QueryEscape(containerName)), nil, "", &snapshots)
	if err != nil {
		return nil, err
	}
	return snapshots, nil
}
func (r *ProtocolLXD) GetContainerSnapshot(containerName string, name string) (*api.ContainerSnapshot, string, error) {
	snapshot := api.ContainerSnapshot{}
	etag, err := r.queryStruct("GET", fmt.Sprintf("/containers/%s/snapshots/%s", url.QueryEscape(containerName), url.QueryEscape(name)), nil, "", &snapshot)
	if err != nil {
		return nil, "", err
	}
	return &snapshot, etag, nil
}