func getPRBuildData(bucket storageBucket, jobs []jobBuilds) []buildData {
	buildch := make(chan buildData)
	defer close(buildch)
	expected := 0
	for _, job := range jobs {
		for j, buildPrefix := range job.buildPrefixes {
			go func(j int, jobName, buildPrefix string) {
				build, err := getBuildData(bucket, buildPrefix)
				if err != nil {
					logrus.WithError(err).Warningf("build %s information incomplete", buildPrefix)
				}
				split := strings.Split(strings.TrimSuffix(buildPrefix, "/"), "/")
				build.SpyglassLink = path.Join(spyglassPrefix, bucket.getName(), buildPrefix)
				build.ID = split[len(split)-1]
				build.jobName = jobName
				build.prefix = buildPrefix
				build.index = j
				buildch <- build
			}(j, job.name, buildPrefix)
			expected++
		}
	}
	builds := []buildData{}
	for k := 0; k < expected; k++ {
		build := <-buildch
		builds = append(builds, build)
	}
	return builds
}

func getPRBuildData ( bucket storageBucket , jobs [ ] jobBuilds ) [ ] buildData { buildch := make ( chan buildData ) defer close ( buildch ) expected := 0 for _ , job := range jobs { for j , buildPrefix := range job . buildPrefixes { go func ( j int , jobName , buildPrefix string ) { build , err := getBuildData ( bucket , buildPrefix ) if err != nil { logrus . WithError ( err ) . Warningf ( " " , buildPrefix ) } split := strings . Split ( strings . TrimSuffix ( buildPrefix , " " ) , " " ) build . SpyglassLink = path . Join ( spyglassPrefix , bucket . getName ( ) , buildPrefix ) build . ID = split [ len ( split ) - 1 ] build . jobName = jobName build . prefix = buildPrefix build . index = j buildch <- build } ( j , job . name , buildPrefix ) expected ++ } } builds := [ ] buildData { } for k := 0 ; k < expected ; k ++ { build := <- buildch builds = append ( builds , build ) } return builds }

// getPRBuildData concurrently fetches metadata on each build of each job run on a PR


func getGCSDirsForPR(config *config.Config, org, repo string, pr int) (map[string]sets.String, error) {
	toSearch := make(map[string]sets.String)
	fullRepo := org + "/" + repo
	presubmits, ok := config.Presubmits[fullRepo]
	if !ok {
		return toSearch, fmt.Errorf("couldn't find presubmits for %q in config", fullRepo)
	}

	for _, presubmit := range presubmits {
		var gcsConfig *v1.GCSConfiguration
		if presubmit.DecorationConfig != nil && presubmit.DecorationConfig.GCSConfiguration != nil {
			gcsConfig = presubmit.DecorationConfig.GCSConfiguration
		} else {
			// for undecorated jobs assume the default
			gcsConfig = config.Plank.DefaultDecorationConfig.GCSConfiguration
		}

		gcsPath, _, _ := gcsupload.PathsForJob(gcsConfig, &downwardapi.JobSpec{
			Type: v1.PresubmitJob,
			Job:  presubmit.Name,
			Refs: &v1.Refs{
				Repo: repo,
				Org:  org,
				Pulls: []v1.Pull{
					{Number: pr},
				},
			},
		}, "")
		gcsPath, _ = path.Split(path.Clean(gcsPath))
		if _, ok := toSearch[gcsConfig.Bucket]; !ok {
			toSearch[gcsConfig.Bucket] = sets.String{}
		}
		toSearch[gcsConfig.Bucket].Insert(gcsPath)
	}
	return toSearch, nil
}

// getGCSDirsForPR returns a map from bucket names -> set of "directories" containing presubmit data

getGCSDirsForPR returns a map from bucket names - > set of directories containing presubmit data


func imageDeleteFromDisk(fingerprint string) {
	// Remove main image file.
	fname := shared.VarPath("images", fingerprint)
	if shared.PathExists(fname) {
		err := os.Remove(fname)
		if err != nil && !os.IsNotExist(err) {
			logger.Errorf("Error deleting image file %s: %s", fname, err)
		}
	}

	// Remove the rootfs file for the image.
	fname = shared.VarPath("images", fingerprint) + ".rootfs"
	if shared.PathExists(fname) {
		err := os.Remove(fname)
		if err != nil && !os.IsNotExist(err) {
			logger.Errorf("Error deleting image file %s: %s", fname, err)
		}
	}
}

// Helper to delete an image file from the local images directory.

Helper to delete an image file from the local images directory .


func doNetworksCreate(d *Daemon, req api.NetworksPost, withDatabase bool) error {
	// Start the network
	n, err := networkLoadByName(d.State(), req.Name)
	if err != nil {
		return err
	}

	err = n.Start()
	if err != nil {
		n.Delete(withDatabase)
		return err
	}

	return nil
}

func
doNetworksCreate
(
d
*
Daemon
,
req
api
.
NetworksPost
,
withDatabase
bool
)
error
{
// Start the network
n
,
err
:=
networkLoadByName
(
d
.
State
(
)
,
req
.
Name
)
if
err
!=
{
return
err
}
err
=
n
.
Start
(
)
if
err
!=
{
n
.
Delete
(
withDatabase
)
return
err
}
return
}

// Create the network on the system. The withDatabase flag is used to decide
// whether to cleanup the database if an error occurs.

Create the network on the system . The withDatabase flag is used to decide whether to cleanup the database if an error occurs .


func networkLoadByName(s *state.State, name string) (*network, error) {
	id, dbInfo, err := s.Cluster.NetworkGet(name)
	if err != nil {
		return nil, err
	}

	n := network{state: s, id: id, name: name, description: dbInfo.Description, config: dbInfo.Config}

	return &n, nil
}

func networkLoadByName ( s * state . State , name string ) ( * network , error ) { id , dbInfo , err := s . Cluster . NetworkGet ( name ) if err != nil { return nil , err } n := network { state : s , id : id , name : name , description : dbInfo . Description , config : dbInfo . Config } return & n , nil }


func (n *Node) Transaction(f func(*NodeTx) error) error {
	nodeTx := &NodeTx{}
	return query.Transaction(n.db, func(tx *sql.Tx) error {
		nodeTx.tx = tx
		return f(nodeTx)
	})
}

func ( n * Node ) Transaction ( f func ( * NodeTx ) error ) error { nodeTx := & NodeTx { } return query . Transaction ( n . db , func ( tx * sql . Tx ) error { nodeTx . tx = tx return f ( nodeTx ) } ) }

// Transaction creates a new NodeTx object and transactionally executes the
// node-level database interactions invoked by the given function. If the
// function returns no error, all database changes are committed to the
// node-level database, otherwise they are rolled back.

Transaction creates a new NodeTx object and transactionally executes the node - level database interactions invoked by the given function . If the function returns no error all database changes are committed to the node - level database otherwise they are rolled back .


func ForLocalInspectionWithPreparedStmts(db *sql.DB) (*Cluster, error) {
	c := ForLocalInspection(db)

	stmts, err := cluster.PrepareStmts(c.db)
	if err != nil {
		return nil, errors.Wrap(err, "Prepare database statements")
	}

	c.stmts = stmts

	return c, nil
}

func ForLocalInspectionWithPreparedStmts ( db * sql . DB ) ( * Cluster , error ) { c := ForLocalInspection ( db ) stmts , err := cluster . PrepareStmts ( c . db ) if err != nil { return nil , errors . Wrap ( err , " " ) } c . stmts = stmts return c , nil }

// ForLocalInspectionWithPreparedStmts is the same as ForLocalInspection but it
// also prepares the statements used in auto-generated database code.

ForLocalInspectionWithPreparedStmts is the same as ForLocalInspection but it also prepares the statements used in auto - generated database code .


func (c *Cluster) SetDefaultTimeout(timeout time.Duration) {
	driver := c.db.Driver().(*dqlite.Driver)
	driver.SetContextTimeout(timeout)
}

func ( c * Cluster ) SetDefaultTimeout ( timeout time . Duration ) { driver := c . db . Driver ( ) . ( * dqlite . Driver ) driver . SetContextTimeout ( timeout ) }

// SetDefaultTimeout sets the default go-dqlite driver timeout.

SetDefaultTimeout sets the default go - dqlite driver timeout .


func (c *Cluster) Transaction(f func(*ClusterTx) error) error {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.transaction(f)
}

func ( c * Cluster ) Transaction ( f func ( * ClusterTx ) error ) error { c . mu . RLock ( ) defer c . mu . RUnlock ( ) return c . transaction ( f ) }

// Transaction creates a new ClusterTx object and transactionally executes the
// cluster database interactions invoked by the given function. If the function
// returns no error, all database changes are committed to the cluster database
// database, otherwise they are rolled back.
//
// If EnterExclusive has been called before, calling Transaction will block
// until ExitExclusive has been called as well to release the lock.

Transaction creates a new ClusterTx object and transactionally executes the cluster database interactions invoked by the given function . If the function returns no error all database changes are committed to the cluster database database otherwise they are rolled back . If EnterExclusive has been called before calling Transaction will block until ExitExclusive has been called as well to release the lock .


func (c *Cluster) EnterExclusive() error {
	logger.Debug("Acquiring exclusive lock on cluster db")

	ch := make(chan struct{})
	go func() {
		c.mu.Lock()
		ch <- struct{}{}
	}()

	timeout := 20 * time.Second
	select {
	case <-ch:
		return nil
	case <-time.After(timeout):
		return fmt.Errorf("timeout (%s)", timeout)
	}
}

func ( c * Cluster ) EnterExclusive ( ) error { logger . Debug ( " " ) ch := make ( chan struct { } ) go func ( ) { c . mu . Lock ( ) ch <- struct { } { } } ( ) timeout := 20 * time . Second select { case <- ch : return nil case <- time . After ( timeout ) : return fmt . Errorf ( " " , timeout ) } }

// EnterExclusive acquires a lock on the cluster db, so any successive call to
// Transaction will block until ExitExclusive has been called.

EnterExclusive acquires a lock on the cluster db so any successive call to Transaction will block until ExitExclusive has been called .


func (c *Cluster) ExitExclusive(f func(*ClusterTx) error) error {
	logger.Debug("Releasing exclusive lock on cluster db")
	defer c.mu.Unlock()
	return c.transaction(f)
}

func ( c * Cluster ) ExitExclusive ( f func ( * ClusterTx ) error ) error { logger . Debug ( " " ) defer c . mu . Unlock ( ) return c . transaction ( f ) }

// ExitExclusive runs the given transaction and then releases the lock acquired
// with EnterExclusive.

ExitExclusive runs the given transaction and then releases the lock acquired with EnterExclusive .


func (c *Cluster) Close() error {
	for _, stmt := range c.stmts {
		stmt.Close()
	}
	return c.db.Close()
}

func ( c * Cluster ) Close ( ) error { for _ , stmt := range c . stmts { stmt . Close ( ) } return c . db . Close ( ) }


func TxCommit(tx *sql.Tx) error {
	err := tx.Commit()
	if err == nil || err == sql.ErrTxDone { // Ignore duplicate commits/rollbacks
		return nil
	}
	return err
}

func
TxCommit
(
tx
*
sql
.
Tx
)
error
{
err
:=
tx
.
Commit
(
)
if
err
==
||
err
==
sql
.
ErrTxDone
{
// Ignore duplicate commits/rollbacks
return
}
return
err
}

// TxCommit commits the given transaction.

TxCommit commits the given transaction .


func (c *Config) ParseRemote(raw string) (string, string, error) {
	result := strings.SplitN(raw, ":", 2)
	if len(result) == 1 {
		return c.DefaultRemote, raw, nil
	}

	_, ok := c.Remotes[result[0]]
	if !ok {
		// Attempt to play nice with snapshots containing ":"
		if shared.IsSnapshot(raw) && shared.IsSnapshot(result[0]) {
			return c.DefaultRemote, raw, nil
		}

		return "", "", fmt.Errorf("The remote \"%s\" doesn't exist", result[0])
	}

	return result[0], result[1], nil
}


func (c *Config) GetContainerServer(name string) (lxd.ContainerServer, error) {
	// Get the remote
	remote, ok := c.Remotes[name]
	if !ok {
		return nil, fmt.Errorf("The remote \"%s\" doesn't exist", name)
	}

	// Sanity checks
	if remote.Public || remote.Protocol == "simplestreams" {
		return nil, fmt.Errorf("The remote isn't a private LXD server")
	}

	// Get connection arguments
	args, err := c.getConnectionArgs(name)
	if err != nil {
		return nil, err
	}

	// Unix socket
	if strings.HasPrefix(remote.Addr, "unix:") {
		d, err := lxd.ConnectLXDUnix(strings.TrimPrefix(strings.TrimPrefix(remote.Addr, "unix:"), "//"), args)
		if err != nil {
			return nil, err
		}

		if remote.Project != "" && remote.Project != "default" {
			d = d.UseProject(remote.Project)
		}

		if c.ProjectOverride != "" {
			d = d.UseProject(c.ProjectOverride)
		}

		return d, nil
	}

	// HTTPs
	if remote.AuthType != "candid" && (args.TLSClientCert == "" || args.TLSClientKey == "") {
		return nil, fmt.Errorf("Missing TLS client certificate and key")
	}

	d, err := lxd.ConnectLXD(remote.Addr, args)
	if err != nil {
		return nil, err
	}

	if remote.Project != "" && remote.Project != "default" {
		d = d.UseProject(remote.Project)
	}

	if c.ProjectOverride != "" {
		d = d.UseProject(c.ProjectOverride)
	}

	return d, nil
}

func
(
c
*
Config
)
GetContainerServer
(
name
string
)
(
lxd
.
ContainerServer
,
error
)
{
// Get the remote
remote
,
ok
:=
c
.
Remotes
[
name
]
if
!
ok
{
return
,
fmt
.
Errorf
(
"
\"
\"
"
,
name
)
}
// Sanity checks
if
remote
.
Public
||
remote
.
Protocol
==
"
"
{
return
,
fmt
.
Errorf
(
"
"
)
}
// Get connection arguments
args
,
err
:=
c
.
getConnectionArgs
(
name
)
if
err
!=
{
return
,
err
}
// Unix socket
if
strings
.
HasPrefix
(
remote
.
Addr
,
"
"
)
{
d
,
err
:=
lxd
.
ConnectLXDUnix
(
strings
.
TrimPrefix
(
strings
.
TrimPrefix
(
remote
.
Addr
,
"
"
)
,
"
"
)
,
args
)
if
err
!=
{
return
,
err
}
if
remote
.
Project
!=
"
"
&&
remote
.
Project
!=
"
"
{
d
=
d
.
UseProject
(
remote
.
Project
)
}
if
c
.
ProjectOverride
!=
"
"
{
d
=
d
.
UseProject
(
c
.
ProjectOverride
)
}
return
d
,
}
// HTTPs
if
remote
.
AuthType
!=
"
"
&&
(
args
.
TLSClientCert
==
"
"
||
args
.
TLSClientKey
==
"
"
)
{
return
,
fmt
.
Errorf
(
"
"
)
}
d
,
err
:=
lxd
.
ConnectLXD
(
remote
.
Addr
,
args
)
if
err
!=
{
return
,
err
}
if
remote
.
Project
!=
"
"
&&
remote
.
Project
!=
"
"
{
d
=
d
.
UseProject
(
remote
.
Project
)
}
if
c
.
ProjectOverride
!=
"
"
{
d
=
d
.
UseProject
(
c
.
ProjectOverride
)
}
return
d
,
}

// GetContainerServer returns a ContainerServer struct for the remote


func (c *Config) GetImageServer(name string) (lxd.ImageServer, error) {
	// Get the remote
	remote, ok := c.Remotes[name]
	if !ok {
		return nil, fmt.Errorf("The remote \"%s\" doesn't exist", name)
	}

	// Get connection arguments
	args, err := c.getConnectionArgs(name)
	if err != nil {
		return nil, err
	}

	// Unix socket
	if strings.HasPrefix(remote.Addr, "unix:") {
		d, err := lxd.ConnectLXDUnix(strings.TrimPrefix(strings.TrimPrefix(remote.Addr, "unix:"), "//"), args)
		if err != nil {
			return nil, err
		}

		if remote.Project != "" && remote.Project != "default" {
			d = d.UseProject(remote.Project)
		}

		if c.ProjectOverride != "" {
			d = d.UseProject(c.ProjectOverride)
		}

		return d, nil
	}

	// HTTPs (simplestreams)
	if remote.Protocol == "simplestreams" {
		d, err := lxd.ConnectSimpleStreams(remote.Addr, args)
		if err != nil {
			return nil, err
		}

		return d, nil
	}

	// HTTPs (public LXD)
	if remote.Public {
		d, err := lxd.ConnectPublicLXD(remote.Addr, args)
		if err != nil {
			return nil, err
		}

		return d, nil
	}

	// HTTPs (private LXD)
	d, err := lxd.ConnectLXD(remote.Addr, args)
	if err != nil {
		return nil, err
	}

	if remote.Project != "" && remote.Project != "default" {
		d = d.UseProject(remote.Project)
	}

	if c.ProjectOverride != "" {
		d = d.UseProject(c.ProjectOverride)
	}

	return d, nil
}

func
(
c
*
Config
)
GetImageServer
(
name
string
)
(
lxd
.
ImageServer
,
error
)
{
// Get the remote
remote
,
ok
:=
c
.
Remotes
[
name
]
if
!
ok
{
return
,
fmt
.
Errorf
(
"
\"
\"
"
,
name
)
}
// Get connection arguments
args
,
err
:=
c
.
getConnectionArgs
(
name
)
if
err
!=
{
return
,
err
}
// Unix socket
if
strings
.
HasPrefix
(
remote
.
Addr
,
"
"
)
{
d
,
err
:=
lxd
.
ConnectLXDUnix
(
strings
.
TrimPrefix
(
strings
.
TrimPrefix
(
remote
.
Addr
,
"
"
)
,
"
"
)
,
args
)
if
err
!=
{
return
,
err
}
if
remote
.
Project
!=
"
"
&&
remote
.
Project
!=
"
"
{
d
=
d
.
UseProject
(
remote
.
Project
)
}
if
c
.
ProjectOverride
!=
"
"
{
d
=
d
.
UseProject
(
c
.
ProjectOverride
)
}
return
d
,
}
// HTTPs (simplestreams)
if
remote
.
Protocol
==
"
"
{
d
,
err
:=
lxd
.
ConnectSimpleStreams
(
remote
.
Addr
,
args
)
if
err
!=
{
return
,
err
}
return
d
,
}
// HTTPs (public LXD)
if
remote
.
Public
{
d
,
err
:=
lxd
.
ConnectPublicLXD
(
remote
.
Addr
,
args
)
if
err
!=
{
return
,
err
}
return
d
,
}
// HTTPs (private LXD)
d
,
err
:=
lxd
.
ConnectLXD
(
remote
.
Addr
,
args
)
if
err
!=
{
return
,
err
}
if
remote
.
Project
!=
"
"
&&
remote
.
Project
!=
"
"
{
d
=
d
.
UseProject
(
remote
.
Project
)
}
if
c
.
ProjectOverride
!=
"
"
{
d
=
d
.
UseProject
(
c
.
ProjectOverride
)
}
return
d
,
}

// GetImageServer returns a ImageServer struct for the remote


func (s *OS) initAppArmor() {
	/* Detect AppArmor availability */
	_, err := exec.LookPath("apparmor_parser")
	if os.Getenv("LXD_SECURITY_APPARMOR") == "false" {
		logger.Warnf("AppArmor support has been manually disabled")
	} else if !shared.IsDir("/sys/kernel/security/apparmor") {
		logger.Warnf("AppArmor support has been disabled because of lack of kernel support")
	} else if err != nil {
		logger.Warnf("AppArmor support has been disabled because 'apparmor_parser' couldn't be found")
	} else {
		s.AppArmorAvailable = true
	}

	/* Detect AppArmor stacking support */
	s.AppArmorStacking = appArmorCanStack()

	/* Detect existing AppArmor stack */
	if shared.PathExists("/sys/kernel/security/apparmor/.ns_stacked") {
		contentBytes, err := ioutil.ReadFile("/sys/kernel/security/apparmor/.ns_stacked")
		if err == nil && string(contentBytes) == "yes\n" {
			s.AppArmorStacked = true
		}
	}

	/* Detect AppArmor admin support */
	if !haveMacAdmin() {
		if s.AppArmorAvailable {
			logger.Warnf("Per-container AppArmor profiles are disabled because the mac_admin capability is missing")
		}
	} else if s.RunningInUserNS && !s.AppArmorStacked {
		if s.AppArmorAvailable {
			logger.Warnf("Per-container AppArmor profiles are disabled because LXD is running in an unprivileged container without stacking")
		}
	} else {
		s.AppArmorAdmin = true
	}

	/* Detect AppArmor confinment */
	profile := util.AppArmorProfile()
	if profile != "unconfined" && profile != "" {
		if s.AppArmorAvailable {
			logger.Warnf("Per-container AppArmor profiles are disabled because LXD is already protected by AppArmor")
		}
		s.AppArmorConfined = true
	}
}

func
(
s
*
OS
)
initAppArmor
(
)
{
/* Detect AppArmor availability */
_
,
err
:=
exec
.
LookPath
(
"
"
)
if
os
.
Getenv
(
"
"
)
==
"
"
{
logger
.
Warnf
(
"
"
)
}
else
if
!
shared
.
IsDir
(
"
"
)
{
logger
.
Warnf
(
"
"
)
}
else
if
err
!=
{
logger
.
Warnf
(
"
"
)
}
else
{
s
.
AppArmorAvailable
=
true
}
/* Detect AppArmor stacking support */
s
.
AppArmorStacking
=
appArmorCanStack
(
)
/* Detect existing AppArmor stack */
if
shared
.
PathExists
(
"
"
)
{
contentBytes
,
err
:=
ioutil
.
ReadFile
(
"
"
)
if
err
==
&&
string
(
contentBytes
)
==
"
\n
"
{
s
.
AppArmorStacked
=
true
}
}
/* Detect AppArmor admin support */
if
!
haveMacAdmin
(
)
{
if
s
.
AppArmorAvailable
{
logger
.
Warnf
(
"
"
)
}
}
else
if
s
.
RunningInUserNS
&&
!
s
.
AppArmorStacked
{
if
s
.
AppArmorAvailable
{
logger
.
Warnf
(
"
"
)
}
}
else
{
s
.
AppArmorAdmin
=
true
}
/* Detect AppArmor confinment */
profile
:=
util
.
AppArmorProfile
(
)
if
profile
!=
"
"
&&
profile
!=
"
"
{
if
s
.
AppArmorAvailable
{
logger
.
Warnf
(
"
"
)
}
s
.
AppArmorConfined
=
true
}
}

// Initialize AppArmor-specific attributes.

Initialize AppArmor - specific attributes .


func appArmorCanStack() bool {
	contentBytes, err := ioutil.ReadFile("/sys/kernel/security/apparmor/features/domain/stack")
	if err != nil {
		return false
	}

	if string(contentBytes) != "yes\n" {
		return false
	}

	contentBytes, err = ioutil.ReadFile("/sys/kernel/security/apparmor/features/domain/version")
	if err != nil {
		return false
	}

	content := string(contentBytes)

	parts := strings.Split(strings.TrimSpace(content), ".")

	if len(parts) == 0 {
		logger.Warn("Unknown apparmor domain version", log.Ctx{"version": content})
		return false
	}

	major, err := strconv.Atoi(parts[0])
	if err != nil {
		logger.Warn("Unknown apparmor domain version", log.Ctx{"version": content})
		return false
	}

	minor := 0
	if len(parts) == 2 {
		minor, err = strconv.Atoi(parts[1])
		if err != nil {
			logger.Warn("Unknown apparmor domain version", log.Ctx{"version": content})
			return false
		}
	}

	return major >= 1 && minor >= 2
}

func appArmorCanStack ( ) bool { contentBytes , err := ioutil . ReadFile ( " " ) if err != nil { return false } if string ( contentBytes ) != " \n " { return false } contentBytes , err = ioutil . ReadFile ( " " ) if err != nil { return false } content := string ( contentBytes ) parts := strings . Split ( strings . TrimSpace ( content ) , " " ) if len ( parts ) == 0 { logger . Warn ( " " , log . Ctx { " " : content } ) return false } major , err := strconv . Atoi ( parts [ 0 ] ) if err != nil { logger . Warn ( " " , log . Ctx { " " : content } ) return false } minor := 0 if len ( parts ) == 2 { minor , err = strconv . Atoi ( parts [ 1 ] ) if err != nil { logger . Warn ( " " , log . Ctx { " " : content } ) return false } } return major >= 1 && minor >= 2 }

// Returns true if AppArmor stacking support is available.

Returns true if AppArmor stacking support is available .


func containerDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the container entry
	container, etag, err := client.GetContainer(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := container.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	container.Devices[devName] = dev

	op, err := client.UpdateContainer(name, container.Writable(), etag)
	if err != nil {
		return err
	}

	return op.Wait()
}

func
containerDeviceAdd
(
client
lxd
.
ContainerServer
,
name
string
,
devName
string
,
dev
map
[
string
]
string
)
error
{
// Get the container entry
container
,
etag
,
err
:=
client
.
GetContainer
(
name
)
if
err
!=
{
return
err
}
// Check if the device already exists
_
,
ok
:=
container
.
Devices
[
devName
]
if
ok
{
return
fmt
.
Errorf
(
i18n
.
G
(
"
"
)
,
devName
)
}
container
.
Devices
[
devName
]
=
dev
op
,
err
:=
client
.
UpdateContainer
(
name
,
container
.
Writable
(
)
,
etag
)
if
err
!=
{
return
err
}
return
op
.
Wait
(
)
}


func profileDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the profile entry
	profile, profileEtag, err := client.GetProfile(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := profile.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	// Add the device to the container
	profile.Devices[devName] = dev

	err = client.UpdateProfile(name, profile.Writable(), profileEtag)
	if err != nil {
		return err
	}

	return nil
}

func
profileDeviceAdd
(
client
lxd
.
ContainerServer
,
name
string
,
devName
string
,
dev
map
[
string
]
string
)
error
{
// Get the profile entry
profile
,
profileEtag
,
err
:=
client
.
GetProfile
(
name
)
if
err
!=
{
return
err
}
// Check if the device already exists
_
,
ok
:=
profile
.
Devices
[
devName
]
if
ok
{
return
fmt
.
Errorf
(
i18n
.
G
(
"
"
)
,
devName
)
}
// Add the device to the container
profile
.
Devices
[
devName
]
=
dev
err
=
client
.
UpdateProfile
(
name
,
profile
.
Writable
(
)
,
profileEtag
)
if
err
!=
{
return
err
}
return
}


func ensureImageAliases(client lxd.ContainerServer, aliases []api.ImageAlias, fingerprint string) error {
	if len(aliases) == 0 {
		return nil
	}

	names := make([]string, len(aliases))
	for i, alias := range aliases {
		names[i] = alias.Name
	}
	sort.Strings(names)

	resp, err := client.GetImageAliases()
	if err != nil {
		return err
	}

	// Delete existing aliases that match provided ones
	for _, alias := range GetExistingAliases(names, resp) {
		err := client.DeleteImageAlias(alias.Name)
		if err != nil {
			fmt.Println(fmt.Sprintf(i18n.G("Failed to remove alias %s"), alias.Name))
		}
	}
	// Create new aliases
	for _, alias := range aliases {
		aliasPost := api.ImageAliasesPost{}
		aliasPost.Name = alias.Name
		aliasPost.Target = fingerprint
		err := client.CreateImageAlias(aliasPost)
		if err != nil {
			fmt.Println(fmt.Sprintf(i18n.G("Failed to create alias %s"), alias.Name))
		}
	}
	return nil
}

// Create the specified image alises, updating those that already exist

Create the specified image alises updating those that already exist


func GetExistingAliases(aliases []string, allAliases []api.ImageAliasesEntry) []api.ImageAliasesEntry {
	existing := []api.ImageAliasesEntry{}
	for _, alias := range allAliases {
		name := alias.Name
		pos := sort.SearchStrings(aliases, name)
		if pos < len(aliases) && aliases[pos] == name {
			existing = append(existing, alias)
		}
	}
	return existing
}

func GetExistingAliases ( aliases [ ] string , allAliases [ ] api . ImageAliasesEntry ) [ ] api . ImageAliasesEntry { existing := [ ] api . ImageAliasesEntry { } for _ , alias := range allAliases { name := alias . Name pos := sort . SearchStrings ( aliases , name ) if pos < len ( aliases ) && aliases [ pos ] == name { existing = append ( existing , alias ) } } return existing }

// GetExistingAliases returns the intersection between a list of aliases and all the existing ones.

GetExistingAliases returns the intersection between a list of aliases and all the existing ones .


func (o StatusCode) String() string {
	return map[StatusCode]string{
		OperationCreated: "Operation created",
		Started:          "Started",
		Stopped:          "Stopped",
		Running:          "Running",
		Cancelling:       "Cancelling",
		Pending:          "Pending",
		Success:          "Success",
		Failure:          "Failure",
		Cancelled:        "Cancelled",
		Starting:         "Starting",
		Stopping:         "Stopping",
		Aborting:         "Aborting",
		Freezing:         "Freezing",
		Frozen:           "Frozen",
		Thawed:           "Thawed",
		Error:            "Error",
	}[o]
}

func ( o StatusCode ) String ( ) string { return map [ StatusCode ] string { OperationCreated : " " , Started : " " , Stopped : " " , Running : " " , Cancelling : " " , Pending : " " , Success : " " , Failure : " " , Cancelled : " " , Starting : " " , Stopping : " " , Aborting : " " , Freezing : " " , Frozen : " " , Thawed : " " , Error : " " , } [ o ] }

// String returns a suitable string representation for the status code


func (c *Cluster) ImagesGetExpired(expiry int64) ([]string, error) {
	q := `SELECT fingerprint, last_use_date, upload_date FROM images WHERE cached=1`

	var fpStr string
	var useStr string
	var uploadStr string

	inargs := []interface{}{}
	outfmt := []interface{}{fpStr, useStr, uploadStr}
	dbResults, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return []string{}, err
	}

	results := []string{}
	for _, r := range dbResults {
		// Figure out the expiry
		timestamp := r[2]
		if r[1] != "" {
			timestamp = r[1]
		}

		var imageExpiry time.Time
		err = imageExpiry.UnmarshalText([]byte(timestamp.(string)))
		if err != nil {
			return []string{}, err
		}
		imageExpiry = imageExpiry.Add(time.Duration(expiry*24) * time.Hour)

		// Check if expired
		if imageExpiry.After(time.Now()) {
			continue
		}

		results = append(results, r[0].(string))
	}

	return results, nil
}

// ImagesGetExpired returns the names of all images that have expired since the
// given time.

ImagesGetExpired returns the names of all images that have expired since the given time .


func (c *Cluster) ImageSourceInsert(id int, server string, protocol string, certificate string, alias string) error {
	stmt := `INSERT INTO images_source (image_id, server, protocol, certificate, alias) values (?, ?, ?, ?, ?)`

	protocolInt := -1
	for protoInt, protoString := range ImageSourceProtocol {
		if protoString == protocol {
			protocolInt = protoInt
		}
	}

	if protocolInt == -1 {
		return fmt.Errorf("Invalid protocol: %s", protocol)
	}

	err := exec(c.db, stmt, id, server, protocolInt, certificate, alias)
	return err
}

// ImageSourceInsert inserts a new image source.

ImageSourceInsert inserts a new image source .


func (c *Cluster) ImageSourceGet(imageID int) (int, api.ImageSource, error) {
	q := `SELECT id, server, protocol, certificate, alias FROM images_source WHERE image_id=?`

	id := 0
	protocolInt := -1
	result := api.ImageSource{}

	arg1 := []interface{}{imageID}
	arg2 := []interface{}{&id, &result.Server, &protocolInt, &result.Certificate, &result.Alias}
	err := dbQueryRowScan(c.db, q, arg1, arg2)
	if err != nil {
		if err == sql.ErrNoRows {
			return -1, api.ImageSource{}, ErrNoSuchObject
		}

		return -1, api.ImageSource{}, err
	}

	protocol, found := ImageSourceProtocol[protocolInt]
	if !found {
		return -1, api.ImageSource{}, fmt.Errorf("Invalid protocol: %d", protocolInt)
	}

	result.Protocol = protocol

	return id, result, nil

}

// ImageSourceGet returns the image source with the given ID.

ImageSourceGet returns the image source with the given ID .


func (c *Cluster) ImageExists(project string, fingerprint string) (bool, error) {
	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasImages(project)
		if err != nil {
			return errors.Wrap(err, "Check if project has images")
		}
		if !enabled {
			project = "default"
		}
		return nil
	})
	if err != nil {
		return false, err
	}

	var exists bool
	query := `
SELECT COUNT(*) > 0
  FROM images
  JOIN projects ON projects.id = images.project_id
 WHERE projects.name = ? AND fingerprint=?
`
	inargs := []interface{}{project, fingerprint}
	outargs := []interface{}{&exists}
	err = dbQueryRowScan(c.db, query, inargs, outargs)
	if err == sql.ErrNoRows {
		return exists, ErrNoSuchObject
	}

	return exists, err
}

// ImageExists returns whether an image with the given fingerprint exists.

ImageExists returns whether an image with the given fingerprint exists .


func (c *Cluster) ImageGet(project, fingerprint string, public bool, strictMatching bool) (int, *api.Image, error) {
	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasImages(project)
		if err != nil {
			return errors.Wrap(err, "Check if project has images")
		}
		if !enabled {
			project = "default"
		}
		return nil
	})
	if err != nil {
		return -1, nil, err
	}

	var create, expire, used, upload *time.Time // These hold the db-returned times

	// The object we'll actually return
	image := api.Image{}
	id := -1
	arch := -1

	// These two humongous things will be filled by the call to DbQueryRowScan
	outfmt := []interface{}{&id, &image.Fingerprint, &image.Filename,
		&image.Size, &image.Cached, &image.Public, &image.AutoUpdate, &arch,
		&create, &expire, &used, &upload}

	inargs := []interface{}{project}
	query := `
        SELECT
            images.id, fingerprint, filename, size, cached, public, auto_update, architecture,
            creation_date, expiry_date, last_use_date, upload_date
        FROM images
        JOIN projects ON projects.id = images.project_id
       WHERE projects.name = ?`
	if strictMatching {
		inargs = append(inargs, fingerprint)
		query += " AND fingerprint = ?"
	} else {
		inargs = append(inargs, fingerprint+"%")
		query += " AND fingerprint LIKE ?"
	}

	if public {
		query += " AND public=1"
	}

	err = dbQueryRowScan(c.db, query, inargs, outfmt)
	if err != nil {
		if err == sql.ErrNoRows {
			return -1, nil, ErrNoSuchObject
		}

		return -1, nil, err // Likely: there are no rows for this fingerprint
	}

	// Validate we only have a single match
	if !strictMatching {
		query = `
SELECT COUNT(images.id)
  FROM images
  JOIN projects ON projects.id = images.project_id
 WHERE projects.name = ?
   AND fingerprint LIKE ?
`
		count := 0
		outfmt := []interface{}{&count}

		err = dbQueryRowScan(c.db, query, inargs, outfmt)
		if err != nil {
			return -1, nil, err
		}

		if count > 1 {
			return -1, nil, fmt.Errorf("Partial fingerprint matches more than one image")
		}
	}

	err = c.imageFill(id, &image, create, expire, used, upload, arch)
	if err != nil {
		return -1, nil, errors.Wrapf(err, "Fill image details")
	}

	return id, &image, nil
}

// ImageGet gets an Image object from the database.
// If strictMatching is false, The fingerprint argument will be queried with a LIKE query, means you can
// pass a shortform and will get the full fingerprint.
// There can never be more than one image with a given fingerprint, as it is
// enforced by a UNIQUE constraint in the schema.

ImageGet gets an Image object from the database . If strictMatching is false The fingerprint argument will be queried with a LIKE query means you can pass a shortform and will get the full fingerprint . There can never be more than one image with a given fingerprint as it is enforced by a UNIQUE constraint in the schema .


func (c *Cluster) ImageGetFromAnyProject(fingerprint string) (int, *api.Image, error) {
	var create, expire, used, upload *time.Time // These hold the db-returned times

	// The object we'll actually return
	image := api.Image{}
	id := -1
	arch := -1

	// These two humongous things will be filled by the call to DbQueryRowScan
	outfmt := []interface{}{&id, &image.Fingerprint, &image.Filename,
		&image.Size, &image.Cached, &image.Public, &image.AutoUpdate, &arch,
		&create, &expire, &used, &upload}

	inargs := []interface{}{fingerprint}
	query := `
        SELECT
            images.id, fingerprint, filename, size, cached, public, auto_update, architecture,
            creation_date, expiry_date, last_use_date, upload_date
        FROM images
        WHERE fingerprint = ?
        LIMIT 1`

	err := dbQueryRowScan(c.db, query, inargs, outfmt)
	if err != nil {
		if err == sql.ErrNoRows {
			return -1, nil, ErrNoSuchObject
		}

		return -1, nil, err // Likely: there are no rows for this fingerprint
	}

	err = c.imageFill(id, &image, create, expire, used, upload, arch)
	if err != nil {
		return -1, nil, errors.Wrapf(err, "Fill image details")
	}

	return id, &image, nil
}

// ImageGetFromAnyProject returns an image matching the given fingerprint, if
// it exists in any project.

ImageGetFromAnyProject returns an image matching the given fingerprint if it exists in any project .


func (c *Cluster) imageFill(id int, image *api.Image, create, expire, used, upload *time.Time, arch int) error {
	// Some of the dates can be nil in the DB, let's process them.
	if create != nil {
		image.CreatedAt = *create
	} else {
		image.CreatedAt = time.Time{}
	}

	if expire != nil {
		image.ExpiresAt = *expire
	} else {
		image.ExpiresAt = time.Time{}
	}

	if used != nil {
		image.LastUsedAt = *used
	} else {
		image.LastUsedAt = time.Time{}
	}

	image.Architecture, _ = osarch.ArchitectureName(arch)

	// The upload date is enforced by NOT NULL in the schema, so it can never be nil.
	image.UploadedAt = *upload

	// Get the properties
	q := "SELECT key, value FROM images_properties where image_id=?"
	var key, value, name, desc string
	inargs := []interface{}{id}
	outfmt := []interface{}{key, value}
	results, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return err
	}

	properties := map[string]string{}
	for _, r := range results {
		key = r[0].(string)
		value = r[1].(string)
		properties[key] = value
	}

	image.Properties = properties

	// Get the aliases
	q = "SELECT name, description FROM images_aliases WHERE image_id=?"
	inargs = []interface{}{id}
	outfmt = []interface{}{name, desc}
	results, err = queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return err
	}

	aliases := []api.ImageAlias{}
	for _, r := range results {
		name = r[0].(string)
		desc = r[1].(string)
		a := api.ImageAlias{Name: name, Description: desc}
		aliases = append(aliases, a)
	}

	image.Aliases = aliases

	_, source, err := c.ImageSourceGet(id)
	if err == nil {
		image.UpdateSource = &source
	}

	return nil
}

// Fill extra image fields such as properties and alias. This is called after
// fetching a single row from the images table.

Fill extra image fields such as properties and alias . This is called after fetching a single row from the images table .


func (c *Cluster) ImageLocate(fingerprint string) (string, error) {
	stmt := `
SELECT nodes.address FROM nodes
  LEFT JOIN images_nodes ON images_nodes.node_id = nodes.id
  LEFT JOIN images ON images_nodes.image_id = images.id
WHERE images.fingerprint = ?
`
	var localAddress string // Address of this node
	var addresses []string  // Addresses of online nodes with the image

	err := c.Transaction(func(tx *ClusterTx) error {
		offlineThreshold, err := tx.NodeOfflineThreshold()
		if err != nil {
			return err
		}

		localAddress, err = tx.NodeAddress()
		if err != nil {
			return err
		}
		allAddresses, err := query.SelectStrings(tx.tx, stmt, fingerprint)
		if err != nil {
			return err
		}
		for _, address := range allAddresses {
			node, err := tx.NodeByAddress(address)
			if err != nil {
				return err
			}
			if address != localAddress && node.IsOffline(offlineThreshold) {
				continue
			}
			addresses = append(addresses, address)
		}
		return err
	})
	if err != nil {
		return "", err
	}
	if len(addresses) == 0 {
		return "", fmt.Errorf("image not available on any online node")
	}

	for _, address := range addresses {
		if address == localAddress {
			return "", nil
		}
	}

	return addresses[0], nil
}

// ImageLocate returns the address of an online node that has a local copy of
// the given image, or an empty string if the image is already available on this
// node.
//
// If the image is not available on any online node, an error is returned.

ImageLocate returns the address of an online node that has a local copy of the given image or an empty string if the image is already available on this node . If the image is not available on any online node an error is returned .


func (c *Cluster) ImageAssociateNode(project, fingerprint string) error {
	imageID, _, err := c.ImageGet(project, fingerprint, false, true)
	if err != nil {
		return err
	}

	err = c.Transaction(func(tx *ClusterTx) error {
		_, err := tx.tx.Exec("INSERT INTO images_nodes(image_id, node_id) VALUES(?, ?)", imageID, c.nodeID)
		return err
	})
	return err
}

func ( c * Cluster ) ImageAssociateNode ( project , fingerprint string ) error { imageID , _ , err := c . ImageGet ( project , fingerprint , false , true ) if err != nil { return err } err = c . Transaction ( func ( tx * ClusterTx ) error { _ , err := tx . tx . Exec ( " " , imageID , c . nodeID ) return err } ) return err }

// ImageAssociateNode creates a new entry in the images_nodes table for
// tracking that the current node has the given image.

ImageAssociateNode creates a new entry in the images_nodes table for tracking that the current node has the given image .


func (c *Cluster) ImageDelete(id int) error {
	err := exec(c.db, "DELETE FROM images WHERE id=?", id)
	if err != nil {
		return err
	}

	return nil
}

func ( c * Cluster ) ImageDelete ( id int ) error { err := exec ( c . db , " " , id ) if err != nil { return err } return nil }

// ImageDelete deletes the image with the given ID.

ImageDelete deletes the image with the given ID .


func (c *Cluster) ImageAliasesGet(project string) ([]string, error) {
	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasImages(project)
		if err != nil {
			return errors.Wrap(err, "Check if project has images")
		}
		if !enabled {
			project = "default"
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	q := `
SELECT images_aliases.name
  FROM images_aliases
  JOIN projects ON projects.id=images_aliases.project_id
 WHERE projects.name=?
`
	var name string
	inargs := []interface{}{project}
	outfmt := []interface{}{name}
	results, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return nil, err
	}
	names := []string{}
	for _, res := range results {
		names = append(names, res[0].(string))
	}
	return names, nil
}

// ImageAliasesGet returns the names of the aliases of all images.

ImageAliasesGet returns the names of the aliases of all images .


func (c *Cluster) ImageAliasGet(project, name string, isTrustedClient bool) (int, api.ImageAliasesEntry, error) {
	id := -1
	entry := api.ImageAliasesEntry{}

	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasImages(project)
		if err != nil {
			return errors.Wrap(err, "Check if project has images")
		}
		if !enabled {
			project = "default"
		}
		return nil
	})
	if err != nil {
		return id, entry, err
	}

	q := `SELECT images_aliases.id, images.fingerprint, images_aliases.description
			 FROM images_aliases
			 INNER JOIN images
			 ON images_aliases.image_id=images.id
                         INNER JOIN projects
                         ON images_aliases.project_id=projects.id
			 WHERE projects.name=? AND images_aliases.name=?`
	if !isTrustedClient {
		q = q + ` AND images.public=1`
	}

	var fingerprint, description string

	arg1 := []interface{}{project, name}
	arg2 := []interface{}{&id, &fingerprint, &description}
	err = dbQueryRowScan(c.db, q, arg1, arg2)
	if err != nil {
		if err == sql.ErrNoRows {
			return -1, entry, ErrNoSuchObject
		}

		return -1, entry, err
	}

	entry.Name = name
	entry.Target = fingerprint
	entry.Description = description

	return id, entry, nil
}

// ImageAliasGet returns the alias with the given name in the given project.

ImageAliasGet returns the alias with the given name in the given project .


func (c *Cluster) ImageAliasRename(id int, name string) error {
	err := exec(c.db, "UPDATE images_aliases SET name=? WHERE id=?", name, id)
	return err
}

func ( c * Cluster ) ImageAliasRename ( id int , name string ) error { err := exec ( c . db , " " , name , id ) return err }

// ImageAliasRename renames the alias with the given ID.

ImageAliasRename renames the alias with the given ID .


func (c *Cluster) ImageAliasDelete(project, name string) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasImages(project)
		if err != nil {
			return errors.Wrap(err, "Check if project has images")
		}
		if !enabled {
			project = "default"
		}
		return nil
	})
	if err != nil {
		return err
	}

	err = exec(c.db, `
DELETE
  FROM images_aliases
 WHERE project_id = (SELECT id FROM projects WHERE name = ?) AND name = ?
`, project, name)
	return err
}

// ImageAliasDelete deletes the alias with the given name.

ImageAliasDelete deletes the alias with the given name .


func (c *Cluster) ImageAliasesMove(source int, destination int) error {
	err := exec(c.db, "UPDATE images_aliases SET image_id=? WHERE image_id=?", destination, source)
	return err
}

func ( c * Cluster ) ImageAliasesMove ( source int , destination int ) error { err := exec ( c . db , " " , destination , source ) return err }

// ImageAliasesMove changes the image ID associated with an alias.

ImageAliasesMove changes the image ID associated with an alias .


func (c *Cluster) ImageAliasAdd(project, name string, imageID int, desc string) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasImages(project)
		if err != nil {
			return errors.Wrap(err, "Check if project has images")
		}
		if !enabled {
			project = "default"
		}
		return nil
	})
	if err != nil {
		return err
	}

	stmt := `
INSERT INTO images_aliases (name, image_id, description, project_id)
     VALUES (?, ?, ?, (SELECT id FROM projects WHERE name = ?))
`
	err = exec(c.db, stmt, name, imageID, desc, project)
	return err
}

// ImageAliasAdd inserts an alias ento the database.

ImageAliasAdd inserts an alias ento the database .


func (c *Cluster) ImageAliasUpdate(id int, imageID int, desc string) error {
	stmt := `UPDATE images_aliases SET image_id=?, description=? WHERE id=?`
	err := exec(c.db, stmt, imageID, desc, id)
	return err
}

// ImageAliasUpdate updates the alias with the given ID.

ImageAliasUpdate updates the alias with the given ID .


func (c *Cluster) ImageLastAccessUpdate(fingerprint string, date time.Time) error {
	stmt := `UPDATE images SET last_use_date=? WHERE fingerprint=?`
	err := exec(c.db, stmt, date, fingerprint)
	return err
}

// ImageLastAccessUpdate updates the last_use_date field of the image with the
// given fingerprint.

ImageLastAccessUpdate updates the last_use_date field of the image with the given fingerprint .


func (c *Cluster) ImageLastAccessInit(fingerprint string) error {
	stmt := `UPDATE images SET cached=1, last_use_date=strftime("%s") WHERE fingerprint=?`
	err := exec(c.db, stmt, fingerprint)
	return err
}

//ImageLastAccessInit inits the last_use_date field of the image with the given fingerprint.

ImageLastAccessInit inits the last_use_date field of the image with the given fingerprint .


func (c *Cluster) ImageUpdate(id int, fname string, sz int64, public bool, autoUpdate bool, architecture string, createdAt time.Time, expiresAt time.Time, properties map[string]string) error {
	arch, err := osarch.ArchitectureId(architecture)
	if err != nil {
		arch = 0
	}

	err = c.Transaction(func(tx *ClusterTx) error {
		publicInt := 0
		if public {
			publicInt = 1
		}

		autoUpdateInt := 0
		if autoUpdate {
			autoUpdateInt = 1
		}

		stmt, err := tx.tx.Prepare(`UPDATE images SET filename=?, size=?, public=?, auto_update=?, architecture=?, creation_date=?, expiry_date=? WHERE id=?`)
		if err != nil {
			return err
		}
		defer stmt.Close()

		_, err = stmt.Exec(fname, sz, publicInt, autoUpdateInt, arch, createdAt, expiresAt, id)
		if err != nil {
			return err
		}

		_, err = tx.tx.Exec(`DELETE FROM images_properties WHERE image_id=?`, id)
		if err != nil {
			return err
		}

		stmt2, err := tx.tx.Prepare(`INSERT INTO images_properties (image_id, type, key, value) VALUES (?, ?, ?, ?)`)
		if err != nil {
			return err
		}
		defer stmt2.Close()

		for key, value := range properties {
			_, err = stmt2.Exec(id, 0, key, value)
			if err != nil {
				return err
			}
		}

		return nil
	})
	return err
}

// ImageUpdate updates the image with the given ID.

ImageUpdate updates the image with the given ID .


func (c *Cluster) ImageInsert(project, fp string, fname string, sz int64, public bool, autoUpdate bool, architecture string, createdAt time.Time, expiresAt time.Time, properties map[string]string) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasImages(project)
		if err != nil {
			return errors.Wrap(err, "Check if project has images")
		}
		if !enabled {
			project = "default"
		}
		return nil
	})
	if err != nil {
		return err
	}

	arch, err := osarch.ArchitectureId(architecture)
	if err != nil {
		arch = 0
	}

	err = c.Transaction(func(tx *ClusterTx) error {
		publicInt := 0
		if public {
			publicInt = 1
		}

		autoUpdateInt := 0
		if autoUpdate {
			autoUpdateInt = 1
		}

		stmt, err := tx.tx.Prepare(`INSERT INTO images (project_id, fingerprint, filename, size, public, auto_update, architecture, creation_date, expiry_date, upload_date) VALUES ((SELECT id FROM projects WHERE name = ?), ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
		if err != nil {
			return err
		}
		defer stmt.Close()

		result, err := stmt.Exec(project, fp, fname, sz, publicInt, autoUpdateInt, arch, createdAt, expiresAt, time.Now().UTC())
		if err != nil {
			return err
		}

		id64, err := result.LastInsertId()
		if err != nil {
			return err
		}
		id := int(id64)

		if len(properties) > 0 {
			pstmt, err := tx.tx.Prepare(`INSERT INTO images_properties (image_id, type, key, value) VALUES (?, 0, ?, ?)`)
			if err != nil {
				return err
			}
			defer pstmt.Close()

			for k, v := range properties {
				// we can assume, that there is just one
				// value per key
				_, err = pstmt.Exec(id, k, v)
				if err != nil {
					return err
				}
			}

		}

		_, err = tx.tx.Exec("INSERT INTO images_nodes(image_id, node_id) VALUES(?, ?)", id, c.nodeID)
		if err != nil {
			return err
		}

		return nil
	})
	return err
}


func (c *Cluster) ImageGetPools(imageFingerprint string) ([]int64, error) {
	poolID := int64(-1)
	query := "SELECT storage_pool_id FROM storage_volumes WHERE node_id=? AND name=? AND type=?"
	inargs := []interface{}{c.nodeID, imageFingerprint, StoragePoolVolumeTypeImage}
	outargs := []interface{}{poolID}

	result, err := queryScan(c.db, query, inargs, outargs)
	if err != nil {
		return []int64{}, err
	}

	poolIDs := []int64{}
	for _, r := range result {
		poolIDs = append(poolIDs, r[0].(int64))
	}

	return poolIDs, nil
}

func ( c * Cluster ) ImageGetPools ( imageFingerprint string ) ( [ ] int64 , error ) { poolID := int64 ( - 1 ) query := " " inargs := [ ] interface { } { c . nodeID , imageFingerprint , StoragePoolVolumeTypeImage } outargs := [ ] interface { } { poolID } result , err := queryScan ( c . db , query , inargs , outargs ) if err != nil { return [ ] int64 { } , err } poolIDs := [ ] int64 { } for _ , r := range result { poolIDs = append ( poolIDs , r [ 0 ] . ( int64 ) ) } return poolIDs , nil }

// ImageGetPools get the names of all storage pools on which a given image exists.

ImageGetPools get the names of all storage pools on which a given image exists .


func (c *Cluster) ImageGetPoolNamesFromIDs(poolIDs []int64) ([]string, error) {
	var poolName string
	query := "SELECT name FROM storage_pools WHERE id=?"

	poolNames := []string{}
	for _, poolID := range poolIDs {
		inargs := []interface{}{poolID}
		outargs := []interface{}{poolName}

		result, err := queryScan(c.db, query, inargs, outargs)
		if err != nil {
			return []string{}, err
		}

		for _, r := range result {
			poolNames = append(poolNames, r[0].(string))
		}
	}

	return poolNames, nil
}

func ( c * Cluster ) ImageGetPoolNamesFromIDs ( poolIDs [ ] int64 ) ( [ ] string , error ) { var poolName string query := " " poolNames := [ ] string { } for _ , poolID := range poolIDs { inargs := [ ] interface { } { poolID } outargs := [ ] interface { } { poolName } result , err := queryScan ( c . db , query , inargs , outargs ) if err != nil { return [ ] string { } , err } for _ , r := range result { poolNames = append ( poolNames , r [ 0 ] . ( string ) ) } } return poolNames , nil }

// ImageGetPoolNamesFromIDs get the names of all storage pools on which a given image exists.

ImageGetPoolNamesFromIDs get the names of all storage pools on which a given image exists .


func (c *Cluster) ImageUploadedAt(id int, uploadedAt time.Time) error {
	err := exec(c.db, "UPDATE images SET upload_date=? WHERE id=?", uploadedAt, id)
	return err
}

func ( c * Cluster ) ImageUploadedAt ( id int , uploadedAt time . Time ) error { err := exec ( c . db , " " , uploadedAt , id ) return err }

// ImageUploadedAt updates the upload_date column and an image row.

ImageUploadedAt updates the upload_date column and an image row .


func (c *Cluster) ImagesGetOnCurrentNode() (map[string][]string, error) {
	return c.ImagesGetByNodeID(c.nodeID)
}

func ( c * Cluster ) ImagesGetOnCurrentNode ( ) ( map [ string ] [ ] string , error ) { return c . ImagesGetByNodeID ( c . nodeID ) }

// ImagesGetOnCurrentNode returns all images that the current LXD node instance has.

ImagesGetOnCurrentNode returns all images that the current LXD node instance has .


func (c *Cluster) ImagesGetByNodeID(id int64) (map[string][]string, error) {
	images := make(map[string][]string) // key is fingerprint, value is list of projects
	err := c.Transaction(func(tx *ClusterTx) error {
		stmt := `
    SELECT images.fingerprint, projects.name FROM images
      LEFT JOIN images_nodes ON images.id = images_nodes.image_id
			LEFT JOIN nodes ON images_nodes.node_id = nodes.id
			LEFT JOIN projects ON images.project_id = projects.id
    WHERE nodes.id = ?
		`
		rows, err := tx.tx.Query(stmt, id)
		if err != nil {
			return err
		}

		var fingerprint string
		var projectName string
		for rows.Next() {
			err := rows.Scan(&fingerprint, &projectName)
			if err != nil {
				return err
			}

			images[fingerprint] = append(images[fingerprint], projectName)
		}

		return rows.Err()
	})
	return images, err
}

// ImagesGetByNodeID returns all images that the LXD node instance has with the given node id.

ImagesGetByNodeID returns all images that the LXD node instance has with the given node id .


func (c *Cluster) ImageGetNodesWithImage(fingerprint string) ([]string, error) {
	q := `
SELECT DISTINCT nodes.address FROM nodes
  LEFT JOIN images_nodes ON images_nodes.node_id = nodes.id
  LEFT JOIN images ON images_nodes.image_id = images.id
WHERE images.fingerprint = ?
	`
	return c.getNodesByImageFingerprint(q, fingerprint)
}

// ImageGetNodesWithImage returns the addresses of online nodes which already have the image.

ImageGetNodesWithImage returns the addresses of online nodes which already have the image .


func (c *Cluster) ImageGetNodesWithoutImage(fingerprint string) ([]string, error) {
	q := `
SELECT DISTINCT nodes.address FROM nodes WHERE nodes.address NOT IN (
  SELECT DISTINCT nodes.address FROM nodes
    LEFT JOIN images_nodes ON images_nodes.node_id = nodes.id
    LEFT JOIN images ON images_nodes.image_id = images.id
  WHERE images.fingerprint = ?)
`
	return c.getNodesByImageFingerprint(q, fingerprint)
}

// ImageGetNodesWithoutImage returns the addresses of online nodes which don't have the image.

ImageGetNodesWithoutImage returns the addresses of online nodes which don t have the image .


func (g *Group) Add(f Func, schedule Schedule) *Task {
	i := len(g.tasks)
	g.tasks = append(g.tasks, Task{
		f:        f,
		schedule: schedule,
		reset:    make(chan struct{}, 16), // Buffered to not block senders
	})
	return &g.tasks[i]
}

func
(
g
*
Group
)
Add
(
f
Func
,
schedule
Schedule
)
*
Task
{
i
:=
len
(
g
.
tasks
)
g
.
tasks
=
append
(
g
.
tasks
,
Task
{
f
:
f
,
schedule
:
schedule
,
reset
:
make
(
chan
struct
{
}
,
16
)
,
// Buffered to not block senders
}
)
return
&
g
.
tasks
[
i
]
}

// Add a new task to the group, returning its index.

Add a new task to the group returning its index .


func (g *Group) Start() {
	ctx := context.Background()
	ctx, g.cancel = context.WithCancel(ctx)
	g.wg.Add(len(g.tasks))

	g.mu.Lock()
	if g.running == nil {
		g.running = make(map[int]bool)
	}
	g.mu.Unlock()

	for i := range g.tasks {
		g.mu.Lock()
		if g.running[i] {
			g.mu.Unlock()
			continue
		}

		g.running[i] = true
		task := g.tasks[i] // Local variable for the closure below.
		g.mu.Unlock()

		go func(i int) {
			task.loop(ctx)
			g.wg.Done()

			g.mu.Lock()
			g.running[i] = false
			g.mu.Unlock()
		}(i)
	}
}


func zfsIsEnabled() bool {
	out, err := exec.LookPath("zfs")
	if err != nil || len(out) == 0 {
		return false
	}

	return true
}

func zfsIsEnabled ( ) bool { out , err := exec . LookPath ( " " ) if err != nil || len ( out ) == 0 { return false } return true }

// zfsIsEnabled returns whether zfs backend is supported.

zfsIsEnabled returns whether zfs backend is supported .


func zfsToolVersionGet() (string, error) {
	// This function is only really ever relevant on Ubuntu as the only
	// distro that ships out of sync tools and kernel modules
	out, err := shared.RunCommand("dpkg-query", "--showformat=${Version}", "--show", "zfsutils-linux")
	if err != nil {
		return "", err
	}

	return strings.TrimSpace(string(out)), nil
}

// zfsToolVersionGet returns the ZFS tools version


func zfsModuleVersionGet() (string, error) {
	var zfsVersion string

	if shared.PathExists("/sys/module/zfs/version") {
		out, err := ioutil.ReadFile("/sys/module/zfs/version")
		if err != nil {
			return "", fmt.Errorf("Could not determine ZFS module version")
		}

		zfsVersion = string(out)
	} else {
		out, err := shared.RunCommand("modinfo", "-F", "version", "zfs")
		if err != nil {
			return "", fmt.Errorf("Could not determine ZFS module version")
		}

		zfsVersion = out
	}

	return strings.TrimSpace(zfsVersion), nil
}

func zfsModuleVersionGet ( ) ( string , error ) { var zfsVersion string if shared . PathExists ( " " ) { out , err := ioutil . ReadFile ( " " ) if err != nil { return " " , fmt . Errorf ( " " ) } zfsVersion = string ( out ) } else { out , err := shared . RunCommand ( " " , " " , " " , " " ) if err != nil { return " " , fmt . Errorf ( " " ) } zfsVersion = out } return strings . TrimSpace ( zfsVersion ) , nil }

// zfsModuleVersionGet returns the ZFS module version


func zfsPoolVolumeCreate(dataset string, properties ...string) (string, error) {
	cmd := []string{"zfs", "create"}

	for _, prop := range properties {
		cmd = append(cmd, []string{"-o", prop}...)
	}

	cmd = append(cmd, []string{"-p", dataset}...)

	return shared.RunCommand(cmd[0], cmd[1:]...)
}

func zfsPoolVolumeCreate ( dataset string , properties ... string ) ( string , error ) { cmd := [ ] string { " " , " " } for _ , prop := range properties { cmd = append ( cmd , [ ] string { " " , prop } ... ) } cmd = append ( cmd , [ ] string { " " , dataset } ... ) return shared . RunCommand ( cmd [ 0 ] , cmd [ 1 : ] ... ) }

// zfsPoolVolumeCreate creates a ZFS dataset with a set of given properties.

zfsPoolVolumeCreate creates a ZFS dataset with a set of given properties .


func zfsPoolVolumeExists(dataset string) (bool, error) {
	output, err := shared.RunCommand(
		"zfs", "list", "-Ho", "name")

	if err != nil {
		return false, err
	}

	for _, name := range strings.Split(output, "\n") {
		if name == dataset {
			return true, nil
		}
	}
	return false, nil
}

func zfsPoolVolumeExists ( dataset string ) ( bool , error ) { output , err := shared . RunCommand ( " " , " " , " " , " " ) if err != nil { return false , err } for _ , name := range strings . Split ( output , " \n " ) { if name == dataset { return true , nil } } return false , nil }

// zfsPoolVolumeExists verifies if a specific ZFS pool or volume exists.

zfsPoolVolumeExists verifies if a specific ZFS pool or volume exists .


func (c *ClusterTx) NetworkIDsNotPending() (map[string]int64, error) {
	networks := []struct {
		id   int64
		name string
	}{}
	dest := func(i int) []interface{} {
		networks = append(networks, struct {
			id   int64
			name string
		}{})
		return []interface{}{&networks[i].id, &networks[i].name}

	}
	stmt, err := c.tx.Prepare("SELECT id, name FROM networks WHERE NOT state=?")
	if err != nil {
		return nil, err
	}
	defer stmt.Close()
	err = query.SelectObjects(stmt, dest, networkPending)
	if err != nil {
		return nil, err
	}
	ids := map[string]int64{}
	for _, network := range networks {
		ids[network.name] = network.id
	}
	return ids, nil
}

func ( c * ClusterTx ) NetworkIDsNotPending ( ) ( map [ string ] int64 , error ) { networks := [ ] struct { id int64 name string } { } dest := func ( i int ) [ ] interface { } { networks = append ( networks , struct { id int64 name string } { } ) return [ ] interface { } { & networks [ i ] . id , & networks [ i ] . name } } stmt , err := c . tx . Prepare ( " " ) if err != nil { return nil , err } defer stmt . Close ( ) err = query . SelectObjects ( stmt , dest , networkPending ) if err != nil { return nil , err } ids := map [ string ] int64 { } for _ , network := range networks { ids [ network . name ] = network . id } return ids , nil }

// NetworkIDsNotPending returns a map associating each network name to its ID.
//
// Pending networks are skipped.

NetworkIDsNotPending returns a map associating each network name to its ID . Pending networks are skipped .


func (c *ClusterTx) NetworkConfigAdd(networkID, nodeID int64, config map[string]string) error {
	return networkConfigAdd(c.tx, networkID, nodeID, config)
}

func ( c * ClusterTx ) NetworkConfigAdd ( networkID , nodeID int64 , config map [ string ] string ) error { return networkConfigAdd ( c . tx , networkID , nodeID , config ) }

// NetworkConfigAdd adds a new entry in the networks_config table


func (c *ClusterTx) NetworkNodeJoin(networkID, nodeID int64) error {
	columns := []string{"network_id", "node_id"}
	values := []interface{}{networkID, nodeID}
	_, err := query.UpsertObject(c.tx, "networks_nodes", columns, values)
	return err
}

func ( c * ClusterTx ) NetworkNodeJoin ( networkID , nodeID int64 ) error { columns := [ ] string { " " , " " } values := [ ] interface { } { networkID , nodeID } _ , err := query . UpsertObject ( c . tx , " " , columns , values ) return err }

// NetworkNodeJoin adds a new entry in the networks_nodes table.
//
// It should only be used when a new node joins the cluster, when it's safe to
// assume that the relevant network has already been created on the joining node,
// and we just need to track it.

NetworkNodeJoin adds a new entry in the networks_nodes table . It should only be used when a new node joins the cluster when it s safe to assume that the relevant network has already been created on the joining node and we just need to track it .


func (c *ClusterTx) NetworkCreatePending(node, name string, conf map[string]string) error {
	// First check if a network with the given name exists, and, if
	// so, that it's in the pending state.
	network := struct {
		id    int64
		state int
	}{}

	var errConsistency error
	dest := func(i int) []interface{} {
		// Sanity check that there is at most one pool with the given name.
		if i != 0 {
			errConsistency = fmt.Errorf("more than one network exists with the given name")
		}
		return []interface{}{&network.id, &network.state}
	}
	stmt, err := c.tx.Prepare("SELECT id, state FROM networks WHERE name=?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	err = query.SelectObjects(stmt, dest, name)
	if err != nil {
		return err
	}
	if errConsistency != nil {
		return errConsistency
	}

	var networkID = network.id
	if networkID == 0 {
		// No existing network with the given name was found, let's create
		// one.
		columns := []string{"name"}
		values := []interface{}{name}
		networkID, err = query.UpsertObject(c.tx, "networks", columns, values)
		if err != nil {
			return err
		}
	} else {
		// Check that the existing network  is in the pending state.
		if network.state != networkPending {
			return fmt.Errorf("network is not in pending state")
		}
	}

	// Get the ID of the node with the given name.
	nodeInfo, err := c.NodeByName(node)
	if err != nil {
		return err
	}

	// Check that no network entry for this node and network exists yet.
	count, err := query.Count(
		c.tx, "networks_nodes", "network_id=? AND node_id=?", networkID, nodeInfo.ID)
	if err != nil {
		return err
	}
	if count != 0 {
		return ErrAlreadyDefined
	}

	// Insert the node-specific configuration.
	columns := []string{"network_id", "node_id"}
	values := []interface{}{networkID, nodeInfo.ID}
	_, err = query.UpsertObject(c.tx, "networks_nodes", columns, values)
	if err != nil {
		return err
	}
	err = c.NetworkConfigAdd(networkID, nodeInfo.ID, conf)
	if err != nil {
		return err
	}

	return nil
}

// NetworkCreatePending creates a new pending network on the node with
// the given name.

NetworkCreatePending creates a new pending network on the node with the given name .


func (c *ClusterTx) NetworkCreated(name string) error {
	return c.networkState(name, networkCreated)
}

func ( c * ClusterTx ) NetworkCreated ( name string ) error { return c . networkState ( name , networkCreated ) }

// NetworkCreated sets the state of the given network to "Created".

NetworkCreated sets the state of the given network to Created .


func (c *ClusterTx) NetworkErrored(name string) error {
	return c.networkState(name, networkErrored)
}

func ( c * ClusterTx ) NetworkErrored ( name string ) error { return c . networkState ( name , networkErrored ) }

// NetworkErrored sets the state of the given network to "Errored".

NetworkErrored sets the state of the given network to Errored .


func (c *Cluster) NetworkGet(name string) (int64, *api.Network, error) {
	description := sql.NullString{}
	id := int64(-1)
	state := 0

	q := "SELECT id, description, state FROM networks WHERE name=?"
	arg1 := []interface{}{name}
	arg2 := []interface{}{&id, &description, &state}
	err := dbQueryRowScan(c.db, q, arg1, arg2)
	if err != nil {
		if err == sql.ErrNoRows {
			return -1, nil, ErrNoSuchObject
		}

		return -1, nil, err
	}

	config, err := c.NetworkConfigGet(id)
	if err != nil {
		return -1, nil, err
	}

	network := api.Network{
		Name:    name,
		Managed: true,
		Type:    "bridge",
	}
	network.Description = description.String
	network.Config = config

	switch state {
	case networkPending:
		network.Status = "Pending"
	case networkCreated:
		network.Status = "Created"
	default:
		network.Status = "Unknown"
	}

	nodes, err := c.networkNodes(id)
	if err != nil {
		return -1, nil, err
	}
	network.Locations = nodes

	return id, &network, nil
}

func ( c * Cluster ) NetworkGet ( name string ) ( int64 , * api . Network , error ) { description := sql . NullString { } id := int64 ( - 1 ) state := 0 q := " " arg1 := [ ] interface { } { name } arg2 := [ ] interface { } { & id , & description , & state } err := dbQueryRowScan ( c . db , q , arg1 , arg2 ) if err != nil { if err == sql . ErrNoRows { return - 1 , nil , ErrNoSuchObject } return - 1 , nil , err } config , err := c . NetworkConfigGet ( id ) if err != nil { return - 1 , nil , err } network := api . Network { Name : name , Managed : true , Type : " " , } network . Description = description . String network . Config = config switch state { case networkPending : network . Status = " " case networkCreated : network . Status = " " default : network . Status = " " } nodes , err := c . networkNodes ( id ) if err != nil { return - 1 , nil , err } network . Locations = nodes return id , & network , nil }

// NetworkGet returns the network with the given name.

NetworkGet returns the network with the given name .


func (c *Cluster) networkNodes(networkID int64) ([]string, error) {
	stmt := `
SELECT nodes.name FROM nodes
  JOIN networks_nodes ON networks_nodes.node_id = nodes.id
  WHERE networks_nodes.network_id = ?
`
	var nodes []string
	err := c.Transaction(func(tx *ClusterTx) error {
		var err error
		nodes, err = query.SelectStrings(tx.tx, stmt, networkID)
		return err
	})
	if err != nil {
		return nil, err
	}
	return nodes, nil
}

// Return the names of the nodes the given network is defined on.

Return the names of the nodes the given network is defined on .


func (c *Cluster) NetworkGetInterface(devName string) (int64, *api.Network, error) {
	id := int64(-1)
	name := ""
	value := ""

	q := "SELECT networks.id, networks.name, networks_config.value FROM networks LEFT JOIN networks_config ON networks.id=networks_config.network_id WHERE networks_config.key=\"bridge.external_interfaces\" AND networks_config.node_id=?"
	arg1 := []interface{}{c.nodeID}
	arg2 := []interface{}{id, name, value}
	result, err := queryScan(c.db, q, arg1, arg2)
	if err != nil {
		return -1, nil, err
	}

	for _, r := range result {
		for _, entry := range strings.Split(r[2].(string), ",") {
			entry = strings.TrimSpace(entry)

			if entry == devName {
				id = r[0].(int64)
				name = r[1].(string)
			}
		}
	}

	if id == -1 {
		return -1, nil, fmt.Errorf("No network found for interface: %s", devName)
	}

	config, err := c.NetworkConfigGet(id)
	if err != nil {
		return -1, nil, err
	}

	network := api.Network{
		Name:    name,
		Managed: true,
		Type:    "bridge",
	}
	network.Config = config

	return id, &network, nil
}

func ( c * Cluster ) NetworkGetInterface ( devName string ) ( int64 , * api . Network , error ) { id := int64 ( - 1 ) name := " " value := " " q := " \" \" " arg1 := [ ] interface { } { c . nodeID } arg2 := [ ] interface { } { id , name , value } result , err := queryScan ( c . db , q , arg1 , arg2 ) if err != nil { return - 1 , nil , err } for _ , r := range result { for _ , entry := range strings . Split ( r [ 2 ] . ( string ) , " " ) { entry = strings . TrimSpace ( entry ) if entry == devName { id = r [ 0 ] . ( int64 ) name = r [ 1 ] . ( string ) } } } if id == - 1 { return - 1 , nil , fmt . Errorf ( " " , devName ) } config , err := c . NetworkConfigGet ( id ) if err != nil { return - 1 , nil , err } network := api . Network { Name : name , Managed : true , Type : " " , } network . Config = config return id , & network , nil }

// NetworkGetInterface returns the network associated with the interface with
// the given name.

NetworkGetInterface returns the network associated with the interface with the given name .


func (c *Cluster) NetworkConfigGet(id int64) (map[string]string, error) {
	var key, value string
	query := `
        SELECT
            key, value
        FROM networks_config
		WHERE network_id=?
                AND (node_id=? OR node_id IS NULL)`
	inargs := []interface{}{id, c.nodeID}
	outfmt := []interface{}{key, value}
	results, err := queryScan(c.db, query, inargs, outfmt)
	if err != nil {
		return nil, fmt.Errorf("Failed to get network '%d'", id)
	}

	if len(results) == 0 {
		/*
		 * If we didn't get any rows here, let's check to make sure the
		 * network really exists; if it doesn't, let's send back a 404.
		 */
		query := "SELECT id FROM networks WHERE id=?"
		var r int
		results, err := queryScan(c.db, query, []interface{}{id}, []interface{}{r})
		if err != nil {
			return nil, err
		}

		if len(results) == 0 {
			return nil, ErrNoSuchObject
		}
	}

	config := map[string]string{}

	for _, r := range results {
		key = r[0].(string)
		value = r[1].(string)

		config[key] = value
	}

	return config, nil
}

// NetworkConfigGet returns the config map of the network with the given ID.

NetworkConfigGet returns the config map of the network with the given ID .


func (c *Cluster) NetworkCreate(name, description string, config map[string]string) (int64, error) {
	var id int64
	err := c.Transaction(func(tx *ClusterTx) error {
		result, err := tx.tx.Exec("INSERT INTO networks (name, description, state) VALUES (?, ?, ?)", name, description, networkCreated)
		if err != nil {
			return err
		}

		id, err := result.LastInsertId()
		if err != nil {
			return err
		}

		// Insert a node-specific entry pointing to ourselves.
		columns := []string{"network_id", "node_id"}
		values := []interface{}{id, c.nodeID}
		_, err = query.UpsertObject(tx.tx, "networks_nodes", columns, values)
		if err != nil {
			return err
		}

		err = networkConfigAdd(tx.tx, id, c.nodeID, config)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		id = -1
	}
	return id, err
}


func (c *Cluster) NetworkUpdate(name, description string, config map[string]string) error {
	id, _, err := c.NetworkGet(name)
	if err != nil {
		return err
	}

	err = c.Transaction(func(tx *ClusterTx) error {
		err = NetworkUpdateDescription(tx.tx, id, description)
		if err != nil {
			return err
		}

		err = NetworkConfigClear(tx.tx, id, c.nodeID)
		if err != nil {
			return err
		}

		err = networkConfigAdd(tx.tx, id, c.nodeID, config)
		if err != nil {
			return err
		}
		return nil
	})

	return err
}

func ( c * Cluster ) NetworkUpdate ( name , description string , config map [ string ] string ) error { id , _ , err := c . NetworkGet ( name ) if err != nil { return err } err = c . Transaction ( func ( tx * ClusterTx ) error { err = NetworkUpdateDescription ( tx . tx , id , description ) if err != nil { return err } err = NetworkConfigClear ( tx . tx , id , c . nodeID ) if err != nil { return err } err = networkConfigAdd ( tx . tx , id , c . nodeID , config ) if err != nil { return err } return nil } ) return err }

// NetworkUpdate updates the network with the given name.

NetworkUpdate updates the network with the given name .


func NetworkUpdateDescription(tx *sql.Tx, id int64, description string) error {
	_, err := tx.Exec("UPDATE networks SET description=? WHERE id=?", description, id)
	return err
}

func NetworkUpdateDescription ( tx * sql . Tx , id int64 , description string ) error { _ , err := tx . Exec ( " " , description , id ) return err }

// NetworkUpdateDescription updates the description of the network with the
// given ID.

NetworkUpdateDescription updates the description of the network with the given ID .


func NetworkConfigClear(tx *sql.Tx, networkID, nodeID int64) error {
	_, err := tx.Exec(
		"DELETE FROM networks_config WHERE network_id=? AND (node_id=? OR node_id IS NULL)",
		networkID, nodeID)
	if err != nil {
		return err
	}

	return nil
}

func NetworkConfigClear ( tx * sql . Tx , networkID , nodeID int64 ) error { _ , err := tx . Exec ( " " , networkID , nodeID ) if err != nil { return err } return nil }

// NetworkConfigClear resets the config of the network with the given ID
// associated with the node with the given ID.

NetworkConfigClear resets the config of the network with the given ID associated with the node with the given ID .


func (c *Cluster) NetworkDelete(name string) error {
	id, _, err := c.NetworkGet(name)
	if err != nil {
		return err
	}

	err = exec(c.db, "DELETE FROM networks WHERE id=?", id)
	if err != nil {
		return err
	}

	return nil
}

func ( c * Cluster ) NetworkDelete ( name string ) error { id , _ , err := c . NetworkGet ( name ) if err != nil { return err } err = exec ( c . db , " " , id ) if err != nil { return err } return nil }

// NetworkDelete deletes the network with the given name.

NetworkDelete deletes the network with the given name .


func (c *Cluster) NetworkRename(oldName string, newName string) error {
	id, _, err := c.NetworkGet(oldName)
	if err != nil {
		return err
	}

	err = c.Transaction(func(tx *ClusterTx) error {
		_, err = tx.tx.Exec("UPDATE networks SET name=? WHERE id=?", newName, id)
		return err
	})

	return err
}

func ( c * Cluster ) NetworkRename ( oldName string , newName string ) error { id , _ , err := c . NetworkGet ( oldName ) if err != nil { return err } err = c . Transaction ( func ( tx * ClusterTx ) error { _ , err = tx . tx . Exec ( " " , newName , id ) return err } ) return err }


func (r *ProtocolLXD) GetContainers() ([]api.Container, error) {
	containers := []api.Container{}

	// Fetch the raw value
	_, err := r.queryStruct("GET", "/containers?recursion=1", nil, "", &containers)
	if err != nil {
		return nil, err
	}

	return containers, nil
}

func
(
r
*
ProtocolLXD
)
GetContainers
(
)
(
[
]
api
.
Container
,
error
)
{
containers
:=
[
]
api
.
Container
{
}
// Fetch the raw value
_
,
err
:=
r
.
queryStruct
(
"
"
,
"
"
,
,
"
"
,
&
containers
)
if
err
!=
{
return
,
err
}
return
containers
,
}

// GetContainers returns a list of containers


func (r *ProtocolLXD) GetContainersFull() ([]api.ContainerFull, error) {
	containers := []api.ContainerFull{}

	if !r.HasExtension("container_full") {
		return nil, fmt.Errorf("The server is missing the required \"container_full\" API extension")
	}

	// Fetch the raw value
	_, err := r.queryStruct("GET", "/containers?recursion=2", nil, "", &containers)
	if err != nil {
		return nil, err
	}

	return containers, nil
}

func
(
r
*
ProtocolLXD
)
GetContainersFull
(
)
(
[
]
api
.
ContainerFull
,
error
)
{
containers
:=
[
]
api
.
ContainerFull
{
}
if
!
r
.
HasExtension
(
"
"
)
{
return
,
fmt
.
Errorf
(
"
\"
\"
"
)
}
// Fetch the raw value
_
,
err
:=
r
.
queryStruct
(
"
"
,
"
"
,
,
"
"
,
&
containers
)
if
err
!=
{
return
,
err
}
return
containers
,
}

// GetContainersFull returns a list of containers including snapshots, backups and state

GetContainersFull returns a list of containers including snapshots backups and state


func (r *ProtocolLXD) GetContainer(name string) (*api.Container, string, error) {
	container := api.Container{}

	// Fetch the raw value
	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
)
GetContainer
(
name
string
)
(
*
api
.
Container
,
string
,
error
)
{
container
:=
api
.
Container
{
}
// Fetch the raw value
etag
,
err
:=
r
.
queryStruct
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
name
)
)
,
,
"
"
,
&
container
)
if
err
!=
{
return
,
"
"
,
err
}
return
&
container
,
etag
,
}

// GetContainer returns the container entry for the provided name


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")
	}

	if args.PoolName == "" {
		// Send the request
		op, _, err := r.queryOperation("POST", "/containers", args.BackupFile, "")
		if err != nil {
			return nil, err
		}

		return op, nil
	}

	if !r.HasExtension("container_backup_override_pool") {
		return nil, fmt.Errorf("The server is missing the required \"container_backup_override_pool\" API extension")
	}

	// Prepare the HTTP request
	reqURL, err := r.setQueryAttributes(fmt.Sprintf("%s/1.0/containers", r.httpHost))
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", reqURL, args.BackupFile)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/octet-stream")
	req.Header.Set("X-LXD-pool", args.PoolName)

	// Set the user agent
	if r.httpUserAgent != "" {
		req.Header.Set("User-Agent", r.httpUserAgent)
	}

	// Send the request
	resp, err := r.do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Handle errors
	response, _, err := lxdParseResponse(resp)
	if err != nil {
		return nil, err
	}

	// Get to the operation
	respOperation, err := response.MetadataAsOperation()
	if err != nil {
		return nil, err
	}

	// Setup an Operation wrapper
	op := operation{
		Operation: *respOperation,
		r:         r,
		chActive:  make(chan bool),
	}

	return &op, nil
}

func
(
r
*
ProtocolLXD
)
CreateContainerFromBackup
(
args
ContainerBackupArgs
)
(
Operation
,
error
)
{
if
!
r
.
HasExtension
(
"
"
)
{
return
,
fmt
.
Errorf
(
"
\"
\"
"
)
}
if
args
.
PoolName
==
"
"
{
// Send the request
op
,
_
,
err
:=
r
.
queryOperation
(
"
"
,
"
"
,
args
.
BackupFile
,
"
"
)
if
err
!=
{
return
,
err
}
return
op
,
}
if
!
r
.
HasExtension
(
"
"
)
{
return
,
fmt
.
Errorf
(
"
\"
\"
"
)
}
// Prepare the HTTP request
reqURL
,
err
:=
r
.
setQueryAttributes
(
fmt
.
Sprintf
(
"
"
,
r
.
httpHost
)
)
if
err
!=
{
return
,
err
}
req
,
err
:=
http
.
NewRequest
(
"
"
,
reqURL
,
args
.
BackupFile
)
if
err
!=
{
return
,
err
}
req
.
Header
.
Set
(
"
"
,
"
"
)
req
.
Header
.
Set
(
"
"
,
args
.
PoolName
)
// Set the user agent
if
r
.
httpUserAgent
!=
"
"
{
req
.
Header
.
Set
(
"
"
,
r
.
httpUserAgent
)
}
// Send the request
resp
,
err
:=
r
.
do
(
req
)
if
err
!=
{
return
,
err
}
defer
resp
.
Body
.
Close
(
)
// Handle errors
response
,
_
,
err
:=
lxdParseResponse
(
resp
)
if
err
!=
{
return
,
err
}
// Get to the operation
respOperation
,
err
:=
response
.
MetadataAsOperation
(
)
if
err
!=
{
return
,
err
}
// Setup an Operation wrapper
op
:=
operation
{
Operation
:
*
respOperation
,
r
:
r
,
chActive
:
make
(
chan
bool
)
,
}
return
&
op
,
}

// CreateContainerFromBackup is a convenience function to make it easier to
// create a container from a backup

CreateContainerFromBackup is a convenience function to make it easier to create a container from a backup


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")
		}
	}

	// Send the request
	op, _, err := r.queryOperation("POST", "/containers", container, "")
	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
(
"
"
)
{
return
,
fmt
.
Errorf
(
"
\"
\"
"
)
}
}
// Send the request
op
,
_
,
err
:=
r
.
queryOperation
(
"
"
,
"
"
,
container
,
"
"
)
if
err
!=
{
return
,
err
}
return
op
,
}

// CreateContainer requests that LXD creates a new container


func (r *ProtocolLXD) CreateContainerFromImage(source ImageServer, image api.Image, req api.ContainersPost) (RemoteOperation, error) {
	// Set the minimal source fields
	req.Source.Type = "image"

	// Optimization for the local image case
	if r == source {
		// Always use fingerprints for local case
		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),
		}

		// Forward targetOp to remote op
		go func() {
			rop.err = rop.targetOp.Wait()
			close(rop.chDone)
		}()

		return &rop, nil
	}

	// Minimal source fields for remote image
	req.Source.Mode = "pull"

	// If we have an alias and the image is public, use that
	if req.Source.Alias != "" && image.Public {
		req.Source.Fingerprint = ""
	} else {
		req.Source.Fingerprint = image.Fingerprint
		req.Source.Alias = ""
	}

	// Get source server connection information
	info, err := source.GetConnectionInfo()
	if err != nil {
		return nil, err
	}

	req.Source.Protocol = info.Protocol
	req.Source.Certificate = info.Certificate

	// Generate secret token if needed
	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)
}

// CreateContainerFromImage is a convenience function to make it easier to create a container from an existing image


func (r *ProtocolLXD) UpdateContainer(name string, container api.ContainerPut, ETag string) (Operation, error) {
	// Send the request
	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
)
UpdateContainer
(
name
string
,
container
api
.
ContainerPut
,
ETag
string
)
(
Operation
,
error
)
{
// Send the request
op
,
_
,
err
:=
r
.
queryOperation
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
name
)
)
,
container
,
ETag
)
if
err
!=
{
return
,
err
}
return
op
,
}

// UpdateContainer updates the container definition


func (r *ProtocolLXD) RenameContainer(name string, container api.ContainerPost) (Operation, error) {
	// Sanity check
	if container.Migration {
		return nil, fmt.Errorf("Can't ask for a migration through RenameContainer")
	}

	// Send the request
	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
)
RenameContainer
(
name
string
,
container
api
.
ContainerPost
)
(
Operation
,
error
)
{
// Sanity check
if
container
.
Migration
{
return
,
fmt
.
Errorf
(
"
"
)
}
// Send the request
op
,
_
,
err
:=
r
.
queryOperation
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
name
)
)
,
container
,
"
"
)
if
err
!=
{
return
,
err
}
return
op
,
}

// RenameContainer requests that LXD renames the container


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")
		}
	}

	// Send the request
	op, _, err := r.queryOperation("POST", fmt.Sprintf("/containers/%s/exec", url.QueryEscape(containerName)), exec, "")
	if err != nil {
		return nil, err
	}
	opAPI := op.Get()

	// Process additional arguments
	if args != nil {
		// Parse the fds
		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)
			}
		}

		// Call the control handler with a connection to the control socket
		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 {
			// Handle interactive sections
			if args.Stdin != nil && args.Stdout != nil {
				// Connect to the websocket
				conn, err := r.GetOperationWebsocket(opAPI.ID, fds["0"])
				if err != nil {
					return nil, err
				}

				// And attach stdin and stdout to it
				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 {
			// Handle non-interactive sessions
			dones := map[int]chan bool{}
			conns := []*websocket.Conn{}

			// Handle stdin
			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)
			}

			// Handle stdout
			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)
			}

			// Handle stderr
			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)
			}

			// Wait for everything to be done
			go func() {
				for i, chDone := range dones {
					// Skip stdin, dealing with it separately below
					if i == 0 {
						continue
					}

					<-chDone
				}

				if fds["0"] != "" {
					if args.Stdin != nil {
						args.Stdin.Close()
					}

					// Empty the stdin channel but don't block on it as
					// stdin may be stuck in Read()
					go func() {
						<-dones[0]
					}()
				}

				for _, conn := range conns {
					conn.Close()
				}

				if args.DataDone != nil {
					close(args.DataDone)
				}
			}()
		}
	}

	return op, nil
}

// ExecContainer requests that LXD spawns a command inside the container


func (r *ProtocolLXD) GetContainerFile(containerName string, path string) (io.ReadCloser, *ContainerFileResponse, error) {
	// Prepare the HTTP request
	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
	}

	requestURL, err = r.setQueryAttributes(requestURL)
	if err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest("GET", requestURL, nil)
	if err != nil {
		return nil, nil, err
	}

	// Set the user agent
	if r.httpUserAgent != "" {
		req.Header.Set("User-Agent", r.httpUserAgent)
	}

	// Send the request
	resp, err := r.do(req)
	if err != nil {
		return nil, nil, err
	}

	// Check the return value for a cleaner error
	if resp.StatusCode != http.StatusOK {
		_, _, err := lxdParseResponse(resp)
		if err != nil {
			return nil, nil, err
		}
	}

	// Parse the headers
	uid, gid, mode, fileType, _ := shared.ParseLXDFileHeaders(resp.Header)
	fileResp := ContainerFileResponse{
		UID:  uid,
		GID:  gid,
		Mode: mode,
		Type: fileType,
	}

	if fileResp.Type == "directory" {
		// Decode the response
		response := api.Response{}
		decoder := json.NewDecoder(resp.Body)

		err = decoder.Decode(&response)
		if err != nil {
			return nil, nil, err
		}

		// Get the file list
		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
}

// GetContainerFile retrieves the provided path from the container


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")
		}
	}

	// Prepare the HTTP request
	requestURL := fmt.Sprintf("%s/1.0/containers/%s/files?path=%s", r.httpHost, url.QueryEscape(containerName), url.QueryEscape(path))

	requestURL, err := r.setQueryAttributes(requestURL)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", requestURL, args.Content)
	if err != nil {
		return err
	}

	// Set the user agent
	if r.httpUserAgent != "" {
		req.Header.Set("User-Agent", r.httpUserAgent)
	}

	// Set the various headers
	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)
	}

	// Send the request
	resp, err := r.do(req)
	if err != nil {
		return err
	}

	// Check the return value for a cleaner error
	_, _, err = lxdParseResponse(resp)
	if err != nil {
		return err
	}

	return nil
}

// CreateContainerFile tells LXD to create a file in the container


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")
	}

	// Send the request
	_, _, 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
)
DeleteContainerFile
(
containerName
string
,
path
string
)
error
{
if
!
r
.
HasExtension
(
"
"
)
{
return
fmt
.
Errorf
(
"
\"
\"
"
)
}
// Send the request
_
,
_
,
err
:=
r
.
query
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
containerName
)
,
url
.
QueryEscape
(
path
)
)
,
,
"
"
)
if
err
!=
{
return
err
}
return
}

// DeleteContainerFile deletes a file in the container


func (r *ProtocolLXD) GetContainerSnapshotNames(containerName string) ([]string, error) {
	urls := []string{}

	// Fetch the raw value
	_, err := r.queryStruct("GET", fmt.Sprintf("/containers/%s/snapshots", url.QueryEscape(containerName)), nil, "", &urls)
	if err != nil {
		return nil, err
	}

	// Parse it
	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
)
GetContainerSnapshotNames
(
containerName
string
)
(
[
]
string
,
error
)
{
urls
:=
[
]
string
{
}
// Fetch the raw value
_
,
err
:=
r
.
queryStruct
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
containerName
)
)
,
,
"
"
,
&
urls
)
if
err
!=
{
return
,
err
}
// Parse it
names
:=
[
]
string
{
}
for
_
,
uri
:=
range
urls
{
fields
:=
strings
.
Split
(
uri
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
containerName
)
)
)
names
=
append
(
names
,
fields
[
len
(
fields
)
1
]
)
}
return
names
,
}

// GetContainerSnapshotNames returns a list of snapshot names for the container


func (r *ProtocolLXD) GetContainerSnapshots(containerName string) ([]api.ContainerSnapshot, error) {
	snapshots := []api.ContainerSnapshot{}

	// Fetch the raw value
	_, 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
)
GetContainerSnapshots
(
containerName
string
)
(
[
]
api
.
ContainerSnapshot
,
error
)
{
snapshots
:=
[
]
api
.
ContainerSnapshot
{
}
// Fetch the raw value
_
,
err
:=
r
.
queryStruct
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
containerName
)
)
,
,
"
"
,
&
snapshots
)
if
err
!=
{
return
,
err
}
return
snapshots
,
}

// GetContainerSnapshots returns a list of snapshots for the container


func (r *ProtocolLXD) GetContainerSnapshot(containerName string, name string) (*api.ContainerSnapshot, string, error) {
	snapshot := api.ContainerSnapshot{}

	// Fetch the raw value
	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
}

func
(
r
*
ProtocolLXD
)
GetContainerSnapshot
(
containerName
string
,
name
string
)
(
*
api
.
ContainerSnapshot
,
string
,
error
)
{
snapshot
:=
api
.
ContainerSnapshot
{
}
// Fetch the raw value
etag
,
err
:=
r
.
queryStruct
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
containerName
)
,
url
.
QueryEscape
(
name
)
)
,
,
"
"
,
&
snapshot
)
if
err
!=
{
return
,
"
"
,
err
}
return
&
snapshot
,
etag
,
}

// GetContainerSnapshot returns a Snapshot struct for the provided container and snapshot names


func (r *ProtocolLXD) CreateContainerSnapshot(containerName string, snapshot api.ContainerSnapshotsPost) (Operation, error) {
	// Validate the request
	if snapshot.ExpiresAt != nil && !r.HasExtension("snapshot_expiry_creation") {
		return nil, fmt.Errorf("The server is missing the required \"snapshot_expiry_creation\" API extension")
	}

	// Send the request
	op, _, err := r.queryOperation("POST", fmt.Sprintf("/containers/%s/snapshots", url.QueryEscape(containerName)), snapshot, "")
	if err != nil {
		return nil, err
	}

	return op, nil
}

func
(
r
*
ProtocolLXD
)
CreateContainerSnapshot
(
containerName
string
,
snapshot
api
.
ContainerSnapshotsPost
)
(
Operation
,
error
)
{
// Validate the request
if
snapshot
.
ExpiresAt
!=
&&
!
r
.
HasExtension
(
"
"
)
{
return
,
fmt
.
Errorf
(
"
\"
\"
"
)
}
// Send the request
op
,
_
,
err
:=
r
.
queryOperation
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
containerName
)
)
,
snapshot
,
"
"
)
if
err
!=
{
return
,
err
}
return
op
,
}

// CreateContainerSnapshot requests that LXD creates a new snapshot for the container


func (r *ProtocolLXD) MigrateContainerSnapshot(containerName string, name string, container api.ContainerSnapshotPost) (Operation, error) {
	// Sanity check
	if !container.Migration {
		return nil, fmt.Errorf("Can't ask for a rename through MigrateContainerSnapshot")
	}

	// Send the request
	op, _, err := r.queryOperation("POST", fmt.Sprintf("/containers/%s/snapshots/%s", url.QueryEscape(containerName), url.QueryEscape(name)), container, "")
	if err != nil {
		return nil, err
	}

	return op, nil
}

func
(
r
*
ProtocolLXD
)
MigrateContainerSnapshot
(
containerName
string
,
name
string
,
container
api
.
ContainerSnapshotPost
)
(
Operation
,
error
)
{
// Sanity check
if
!
container
.
Migration
{
return
,
fmt
.
Errorf
(
"
"
)
}
// Send the request
op
,
_
,
err
:=
r
.
queryOperation
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
containerName
)
,
url
.
QueryEscape
(
name
)
)
,
container
,
"
"
)
if
err
!=
{
return
,
err
}
return
op
,
}

// MigrateContainerSnapshot requests that LXD prepares for a snapshot migration


func (r *ProtocolLXD) UpdateContainerSnapshot(containerName string, name string, container api.ContainerSnapshotPut, ETag string) (Operation, error) {
	if !r.HasExtension("snapshot_expiry") {
		return nil, fmt.Errorf("The server is missing the required \"snapshot_expiry\" API extension")
	}

	// Send the request
	op, _, err := r.queryOperation("PUT", fmt.Sprintf("/containers/%s/snapshots/%s",
		url.QueryEscape(containerName), url.QueryEscape(name)), container, ETag)
	if err != nil {
		return nil, err
	}

	return op, nil
}

func
(
r
*
ProtocolLXD
)
UpdateContainerSnapshot
(
containerName
string
,
name
string
,
container
api
.
ContainerSnapshotPut
,
ETag
string
)
(
Operation
,
error
)
{
if
!
r
.
HasExtension
(
"
"
)
{
return
,
fmt
.
Errorf
(
"
\"
\"
"
)
}
// Send the request
op
,
_
,
err
:=
r
.
queryOperation
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
containerName
)
,
url
.
QueryEscape
(
name
)
)
,
container
,
ETag
)
if
err
!=
{
return
,
err
}
return
op
,
}

// UpdateContainerSnapshot requests that LXD updates the container snapshot


func (r *ProtocolLXD) GetContainerState(name string) (*api.ContainerState, string, error) {
	state := api.ContainerState{}

	// Fetch the raw value
	etag, err := r.queryStruct("GET", fmt.Sprintf("/containers/%s/state", url.QueryEscape(name)), nil, "", &state)
	if err != nil {
		return nil, "", err
	}

	return &state, etag, nil
}

func
(
r
*
ProtocolLXD
)
GetContainerState
(
name
string
)
(
*
api
.
ContainerState
,
string
,
error
)
{
state
:=
api
.
ContainerState
{
}
// Fetch the raw value
etag
,
err
:=
r
.
queryStruct
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
name
)
)
,
,
"
"
,
&
state
)
if
err
!=
{
return
,
"
"
,
err
}
return
&
state
,
etag
,
}

// GetContainerState returns a ContainerState entry for the provided container name


func (r *ProtocolLXD) UpdateContainerState(name string, state api.ContainerStatePut, ETag string) (Operation, error) {
	// Send the request
	op, _, err := r.queryOperation("PUT", fmt.Sprintf("/containers/%s/state", url.QueryEscape(name)), state, ETag)
	if err != nil {
		return nil, err
	}

	return op, nil
}

func
(
r
*
ProtocolLXD
)
UpdateContainerState
(
name
string
,
state
api
.
ContainerStatePut
,
ETag
string
)
(
Operation
,
error
)
{
// Send the request
op
,
_
,
err
:=
r
.
queryOperation
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
name
)
)
,
state
,
ETag
)
if
err
!=
{
return
,
err
}
return
op
,
}

// UpdateContainerState updates the container to match the requested state


func (r *ProtocolLXD) GetContainerLogfiles(name string) ([]string, error) {
	urls := []string{}

	// Fetch the raw value
	_, err := r.queryStruct("GET", fmt.Sprintf("/containers/%s/logs", url.QueryEscape(name)), nil, "", &urls)
	if err != nil {
		return nil, err
	}

	// Parse it
	logfiles := []string{}
	for _, uri := range logfiles {
		fields := strings.Split(uri, fmt.Sprintf("/containers/%s/logs/", url.QueryEscape(name)))
		logfiles = append(logfiles, fields[len(fields)-1])
	}

	return logfiles, nil
}

func
(
r
*
ProtocolLXD
)
GetContainerLogfiles
(
name
string
)
(
[
]
string
,
error
)
{
urls
:=
[
]
string
{
}
// Fetch the raw value
_
,
err
:=
r
.
queryStruct
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
name
)
)
,
,
"
"
,
&
urls
)
if
err
!=
{
return
,
err
}
// Parse it
logfiles
:=
[
]
string
{
}
for
_
,
uri
:=
range
logfiles
{
fields
:=
strings
.
Split
(
uri
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
name
)
)
)
logfiles
=
append
(
logfiles
,
fields
[
len
(
fields
)
1
]
)
}
return
logfiles
,
}

// GetContainerLogfiles returns a list of logfiles for the container


func (r *ProtocolLXD) GetContainerLogfile(name string, filename string) (io.ReadCloser, error) {
	// Prepare the HTTP request
	url := fmt.Sprintf("%s/1.0/containers/%s/logs/%s", r.httpHost, url.QueryEscape(name), url.QueryEscape(filename))

	url, err := r.setQueryAttributes(url)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	// Set the user agent
	if r.httpUserAgent != "" {
		req.Header.Set("User-Agent", r.httpUserAgent)
	}

	// Send the request
	resp, err := r.do(req)
	if err != nil {
		return nil, err
	}

	// Check the return value for a cleaner error
	if resp.StatusCode != http.StatusOK {
		_, _, err := lxdParseResponse(resp)
		if err != nil {
			return nil, err
		}
	}

	return resp.Body, err
}

// GetContainerLogfile returns the content of the requested logfile
//
// Note that it's the caller's responsibility to close the returned ReadCloser

GetContainerLogfile returns the content of the requested logfile Note that it s the caller s responsibility to close the returned ReadCloser