func projectCreateDefaultProfile(tx *db.ClusterTx, project string) error {
	// Create a default profile
	profile := db.Profile{}
	profile.Project = project
	profile.Name = "default"
	profile.Description = fmt.Sprintf("Default LXD profile for project %s", project)
	profile.Config = map[string]string{}
	profile.Devices = types.Devices{}

	_, err := tx.ProfileCreate(profile)
	if err != nil {
		return errors.Wrap(err, "Add default profile to database")
	}
	return nil
}

func
projectCreateDefaultProfile
(
tx
*
db
.
ClusterTx
,
project
string
)
error
{
// Create a default profile
profile
:=
db
.
Profile
{
}
profile
.
Project
=
project
profile
.
Name
=
"
"
profile
.
Description
=
fmt
.
Sprintf
(
"
"
,
project
)
profile
.
Config
=
map
[
string
]
string
{
}
profile
.
Devices
=
types
.
Devices
{
}
_
,
err
:=
tx
.
ProfileCreate
(
profile
)
if
err
!=
{
return
errors
.
Wrap
(
err
,
"
"
)
}
return
}

// Create the default profile of a project.

Create the default profile of a project .


func projectChange(d *Daemon, project *api.Project, req api.ProjectPut) Response {
	// Flag indicating if any feature has changed.
	featuresChanged := req.Config["features.images"] != project.Config["features.images"] || req.Config["features.profiles"] != project.Config["features.profiles"]

	// Sanity checks
	if project.Name == "default" && featuresChanged {
		return BadRequest(fmt.Errorf("You can't change the features of the default project"))
	}

	if !projectIsEmpty(project) && featuresChanged {
		return BadRequest(fmt.Errorf("Features can only be changed on empty projects"))
	}

	// Validate the configuration
	err := projectValidateConfig(req.Config)
	if err != nil {
		return BadRequest(err)
	}

	// Update the database entry
	err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
		err := tx.ProjectUpdate(project.Name, req)
		if err != nil {
			return errors.Wrap(err, "Persist profile changes")
		}

		if req.Config["features.profiles"] != project.Config["features.profiles"] {
			if req.Config["features.profiles"] == "true" {
				err = projectCreateDefaultProfile(tx, project.Name)
				if err != nil {
					return err
				}
			} else {
				// Delete the project-specific default profile.
				err = tx.ProfileDelete(project.Name, "default")
				if err != nil {
					return errors.Wrap(err, "Delete project default profile")
				}
			}
		}

		return nil

	})

	if err != nil {
		return SmartError(err)
	}

	return EmptySyncResponse
}


func projectIsEmpty(project *api.Project) bool {
	if len(project.UsedBy) > 0 {
		// Check if the only entity is the default profile.
		if len(project.UsedBy) == 1 && strings.Contains(project.UsedBy[0], "/profiles/default") {
			return true
		}
		return false
	}
	return true
}


func (r *ProtocolLXD) GetCertificateFingerprints() ([]string, error) {
	certificates := []string{}

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

	// Parse it
	fingerprints := []string{}
	for _, fingerprint := range certificates {
		fields := strings.Split(fingerprint, "/certificates/")
		fingerprints = append(fingerprints, fields[len(fields)-1])
	}

	return fingerprints, nil
}

func
(
r
*
ProtocolLXD
)
GetCertificateFingerprints
(
)
(
[
]
string
,
error
)
{
certificates
:=
[
]
string
{
}
// Fetch the raw value
_
,
err
:=
r
.
queryStruct
(
"
"
,
"
"
,
,
"
"
,
&
certificates
)
if
err
!=
{
return
,
err
}
// Parse it
fingerprints
:=
[
]
string
{
}
for
_
,
fingerprint
:=
range
certificates
{
fields
:=
strings
.
Split
(
fingerprint
,
"
"
)
fingerprints
=
append
(
fingerprints
,
fields
[
len
(
fields
)
1
]
)
}
return
fingerprints
,
}

// Certificate handling functions
// GetCertificateFingerprints returns a list of certificate fingerprints

Certificate handling functions GetCertificateFingerprints returns a list of certificate fingerprints


func (r *ProtocolLXD) GetCertificates() ([]api.Certificate, error) {
	certificates := []api.Certificate{}

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

	return certificates, nil
}

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

// GetCertificates returns a list of certificates


func (r *ProtocolLXD) GetCertificate(fingerprint string) (*api.Certificate, string, error) {
	certificate := api.Certificate{}

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

	return &certificate, etag, nil
}

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

// GetCertificate returns the certificate entry for the provided fingerprint


func (r *ProtocolLXD) CreateCertificate(certificate api.CertificatesPost) error {
	// Send the request
	_, _, err := r.query("POST", "/certificates", certificate, "")
	if err != nil {
		return err
	}

	return nil
}

func
(
r
*
ProtocolLXD
)
CreateCertificate
(
certificate
api
.
CertificatesPost
)
error
{
// Send the request
_
,
_
,
err
:=
r
.
query
(
"
"
,
"
"
,
certificate
,
"
"
)
if
err
!=
{
return
err
}
return
}

// CreateCertificate adds a new certificate to the LXD trust store


func (r *ProtocolLXD) UpdateCertificate(fingerprint string, certificate api.CertificatePut, ETag string) error {
	if !r.HasExtension("certificate_update") {
		return fmt.Errorf("The server is missing the required \"certificate_update\" API extension")
	}

	// Send the request
	_, _, err := r.query("PUT", fmt.Sprintf("/certificates/%s", url.QueryEscape(fingerprint)), certificate, ETag)
	if err != nil {
		return err
	}

	return nil
}

func
(
r
*
ProtocolLXD
)
UpdateCertificate
(
fingerprint
string
,
certificate
api
.
CertificatePut
,
ETag
string
)
error
{
if
!
r
.
HasExtension
(
"
"
)
{
return
fmt
.
Errorf
(
"
\"
\"
"
)
}
// Send the request
_
,
_
,
err
:=
r
.
query
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
fingerprint
)
)
,
certificate
,
ETag
)
if
err
!=
{
return
err
}
return
}

// UpdateCertificate updates the certificate definition


func (r *ProtocolLXD) DeleteCertificate(fingerprint string) error {
	// Send the request
	_, _, err := r.query("DELETE", fmt.Sprintf("/certificates/%s", url.QueryEscape(fingerprint)), nil, "")
	if err != nil {
		return err
	}

	return nil
}

func
(
r
*
ProtocolLXD
)
DeleteCertificate
(
fingerprint
string
)
error
{
// Send the request
_
,
_
,
err
:=
r
.
query
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
fingerprint
)
)
,
,
"
"
)
if
err
!=
{
return
err
}
return
}

// DeleteCertificate removes a certificate from the LXD trust store


func containerMetadataTemplatesGet(d *Daemon, r *http.Request) Response {
	project := projectParam(r)
	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// Look at the request
	templateName := r.FormValue("path")
	if templateName == "" {
		// List templates
		templatesPath := filepath.Join(c.Path(), "templates")
		filesInfo, err := ioutil.ReadDir(templatesPath)
		if err != nil {
			return InternalError(err)
		}

		templates := []string{}
		for _, info := range filesInfo {
			if !info.IsDir() {
				templates = append(templates, info.Name())
			}
		}

		return SyncResponse(true, templates)
	}

	// Check if the template exists
	templatePath, err := getContainerTemplatePath(c, templateName)
	if err != nil {
		return SmartError(err)
	}

	if !shared.PathExists(templatePath) {
		return NotFound(fmt.Errorf("Path '%s' not found", templatePath))
	}

	// Create a temporary file with the template content (since the container
	// storage might not be available when the file is read from FileResponse)
	template, err := os.Open(templatePath)
	if err != nil {
		return SmartError(err)
	}
	defer template.Close()

	tempfile, err := ioutil.TempFile("", "lxd_template")
	if err != nil {
		return SmartError(err)
	}
	defer tempfile.Close()

	_, err = io.Copy(tempfile, template)
	if err != nil {
		return InternalError(err)
	}

	files := make([]fileResponseEntry, 1)
	files[0].identifier = templateName
	files[0].path = tempfile.Name()
	files[0].filename = templateName
	return FileResponse(r, files, nil, true)
}

// Return a list of templates used in a container or the content of a template


func containerMetadataTemplatesPostPut(d *Daemon, r *http.Request) Response {
	project := projectParam(r)
	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// Look at the request
	templateName := r.FormValue("path")
	if templateName == "" {
		return BadRequest(fmt.Errorf("missing path argument"))
	}

	// Check if the template already exists
	templatePath, err := getContainerTemplatePath(c, templateName)
	if err != nil {
		return SmartError(err)
	}

	if r.Method == "POST" && shared.PathExists(templatePath) {
		return BadRequest(fmt.Errorf("Template already exists"))
	}

	// Write the new template
	template, err := os.OpenFile(templatePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return SmartError(err)
	}
	defer template.Close()

	_, err = io.Copy(template, r.Body)
	if err != nil {
		return InternalError(err)
	}

	return EmptySyncResponse
}


func containerMetadataTemplatesDelete(d *Daemon, r *http.Request) Response {
	project := projectParam(r)

	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// Look at the request
	templateName := r.FormValue("path")
	if templateName == "" {
		return BadRequest(fmt.Errorf("missing path argument"))
	}

	templatePath, err := getContainerTemplatePath(c, templateName)
	if err != nil {
		return SmartError(err)
	}

	if !shared.PathExists(templatePath) {
		return NotFound(fmt.Errorf("Path '%s' not found", templatePath))
	}

	// Delete the template
	err = os.Remove(templatePath)
	if err != nil {
		return InternalError(err)
	}

	return EmptySyncResponse
}


func getContainerTemplatePath(c container, filename string) (string, error) {
	if strings.Contains(filename, "/") {
		return "", fmt.Errorf("Invalid template filename")
	}

	return filepath.Join(c.Path(), "templates", filename), nil
}

func getContainerTemplatePath ( c container , filename string ) ( string , error ) { if strings . Contains ( filename , " " ) { return " " , fmt . Errorf ( " " ) } return filepath . Join ( c . Path ( ) , " " , filename ) , nil }

// Return the full path of a container template.

Return the full path of a container template .


func (e Error) Error() string {
	message := fmt.Sprintf("cannot set '%s'", e.Name)
	if e.Value != nil {
		message += fmt.Sprintf(" to '%v'", e.Value)
	}
	return message + fmt.Sprintf(": %s", e.Reason)
}

func ( e Error ) Error ( ) string { message := fmt . Sprintf ( " " , e . Name ) if e . Value != nil { message += fmt . Sprintf ( " " , e . Value ) } return message + fmt . Sprintf ( " " , e . Reason ) }

// Error implements the error interface.


func (l ErrorList) Error() string {
	switch len(l) {
	case 0:
		return "no errors"
	case 1:
		return l[0].Error()
	}
	return fmt.Sprintf("%s (and %d more errors)", l[0], len(l)-1)
}

func ( l ErrorList ) Error ( ) string { switch len ( l ) { case 0 : return " " case 1 : return l [ 0 ] . Error ( ) } return fmt . Sprintf ( " " , l [ 0 ] , len ( l ) - 1 ) }

// ErrorList implements the error interface.

ErrorList implements the error interface .


func (l *ErrorList) add(name string, value interface{}, reason string) {
	*l = append(*l, &Error{name, value, reason})
}

func ( l * ErrorList ) add ( name string , value interface { } , reason string ) { * l = append ( * l , & Error { name , value , reason } ) }

// Add adds an Error with given key name, value and reason.

Add adds an Error with given key name value and reason .


func UpdateSchema() error {
	err := cluster.SchemaDotGo()
	if err != nil {
		return errors.Wrap(err, "Update cluster database schema")
	}

	err = node.SchemaDotGo()
	if err != nil {
		return errors.Wrap(err, "Update node database schema")
	}

	return nil
}

func UpdateSchema ( ) error { err := cluster . SchemaDotGo ( ) if err != nil { return errors . Wrap ( err , " " ) } err = node . SchemaDotGo ( ) if err != nil { return errors . Wrap ( err , " " ) } return nil }

// UpdateSchema updates the schema.go file of the cluster and node databases.

UpdateSchema updates the schema . go file of the cluster and node databases .


func doProfileUpdateCluster(d *Daemon, project, name string, old api.ProfilePut) error {
	nodeName := ""
	err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
		var err error
		nodeName, err = tx.NodeName()
		return err
	})
	if err != nil {
		return errors.Wrap(err, "failed to query local node name")
	}

	containers, err := getProfileContainersInfo(d.cluster, project, name)
	if err != nil {
		return errors.Wrapf(err, "failed to query containers associated with profile '%s'", name)
	}

	failures := map[string]error{}
	for _, args := range containers {
		err := doProfileUpdateContainer(d, name, old, nodeName, args)
		if err != nil {
			failures[args.Name] = err
		}
	}

	if len(failures) != 0 {
		msg := "The following containers failed to update (profile change still saved):\n"
		for cname, err := range failures {
			msg += fmt.Sprintf(" - %s: %s\n", cname, err)
		}
		return fmt.Errorf("%s", msg)
	}

	return nil
}

func doProfileUpdateCluster ( d * Daemon , project , name string , old api . ProfilePut ) error { nodeName := " " err := d . cluster . Transaction ( func ( tx * db . ClusterTx ) error { var err error nodeName , err = tx . NodeName ( ) return err } ) if err != nil { return errors . Wrap ( err , " " ) } containers , err := getProfileContainersInfo ( d . cluster , project , name ) if err != nil { return errors . Wrapf ( err , " " , name ) } failures := map [ string ] error { } for _ , args := range containers { err := doProfileUpdateContainer ( d , name , old , nodeName , args ) if err != nil { failures [ args . Name ] = err } } if len ( failures ) != 0 { msg := " \n " for cname , err := range failures { msg += fmt . Sprintf ( " \n " , cname , err ) } return fmt . Errorf ( " " , msg ) } return nil }

// Like doProfileUpdate but does not update the database, since it was already
// updated by doProfileUpdate itself, called on the notifying node.

Like doProfileUpdate but does not update the database since it was already updated by doProfileUpdate itself called on the notifying node .


func doProfileUpdateContainer(d *Daemon, name string, old api.ProfilePut, nodeName string, args db.ContainerArgs) error {
	if args.Node != "" && args.Node != nodeName {
		// No-op, this container does not belong to this node.
		return nil
	}

	profiles, err := d.cluster.ProfilesGet(args.Project, args.Profiles)
	if err != nil {
		return err
	}
	for i, profileName := range args.Profiles {
		if profileName == name {
			// Use the old config and devices.
			profiles[i].Config = old.Config
			profiles[i].Devices = old.Devices
			break
		}
	}

	c := containerLXCInstantiate(d.State(), args)

	c.expandConfig(profiles)
	c.expandDevices(profiles)

	return c.Update(db.ContainerArgs{
		Architecture: c.Architecture(),
		Config:       c.LocalConfig(),
		Description:  c.Description(),
		Devices:      c.LocalDevices(),
		Ephemeral:    c.IsEphemeral(),
		Profiles:     c.Profiles(),
		Project:      c.Project(),
	}, true)
}

// Profile update of a single container.


func getProfileContainersInfo(cluster *db.Cluster, project, profile string) ([]db.ContainerArgs, error) {
	// Query the db for information about containers associated with the
	// given profile.
	names, err := cluster.ProfileContainersGet(project, profile)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to query containers with profile '%s'", profile)
	}

	containers := []db.ContainerArgs{}
	err = cluster.Transaction(func(tx *db.ClusterTx) error {
		for ctProject, ctNames := range names {
			for _, ctName := range ctNames {
				container, err := tx.ContainerGet(ctProject, ctName)
				if err != nil {
					return err
				}

				containers = append(containers, db.ContainerToArgs(container))
			}
		}

		return nil
	})
	if err != nil {
		return nil, errors.Wrapf(err, "Failed to fetch containers")
	}

	return containers, nil
}

// Query the db for information about containers associated with the given
// profile.

Query the db for information about containers associated with the given profile .


func (r *ProtocolLXD) GetNetworkNames() ([]string, error) {
	if !r.HasExtension("network") {
		return nil, fmt.Errorf("The server is missing the required \"network\" API extension")
	}

	urls := []string{}

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

	// Parse it
	names := []string{}
	for _, url := range urls {
		fields := strings.Split(url, "/networks/")
		names = append(names, fields[len(fields)-1])
	}

	return names, nil
}

func
(
r
*
ProtocolLXD
)
GetNetworkNames
(
)
(
[
]
string
,
error
)
{
if
!
r
.
HasExtension
(
"
"
)
{
return
,
fmt
.
Errorf
(
"
\"
\"
"
)
}
urls
:=
[
]
string
{
}
// Fetch the raw value
_
,
err
:=
r
.
queryStruct
(
"
"
,
"
"
,
,
"
"
,
&
urls
)
if
err
!=
{
return
,
err
}
// Parse it
names
:=
[
]
string
{
}
for
_
,
url
:=
range
urls
{
fields
:=
strings
.
Split
(
url
,
"
"
)
names
=
append
(
names
,
fields
[
len
(
fields
)
1
]
)
}
return
names
,
}

// GetNetworkNames returns a list of network names


func (r *ProtocolLXD) GetNetworks() ([]api.Network, error) {
	if !r.HasExtension("network") {
		return nil, fmt.Errorf("The server is missing the required \"network\" API extension")
	}

	networks := []api.Network{}

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

	return networks, nil
}

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

// GetNetworks returns a list of Network struct


func (r *ProtocolLXD) GetNetwork(name string) (*api.Network, string, error) {
	if !r.HasExtension("network") {
		return nil, "", fmt.Errorf("The server is missing the required \"network\" API extension")
	}

	network := api.Network{}

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

	return &network, etag, nil
}

func
(
r
*
ProtocolLXD
)
GetNetwork
(
name
string
)
(
*
api
.
Network
,
string
,
error
)
{
if
!
r
.
HasExtension
(
"
"
)
{
return
,
"
"
,
fmt
.
Errorf
(
"
\"
\"
"
)
}
network
:=
api
.
Network
{
}
// Fetch the raw value
etag
,
err
:=
r
.
queryStruct
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
name
)
)
,
,
"
"
,
&
network
)
if
err
!=
{
return
,
"
"
,
err
}
return
&
network
,
etag
,
}

// GetNetwork returns a Network entry for the provided name


func (r *ProtocolLXD) GetNetworkLeases(name string) ([]api.NetworkLease, error) {
	if !r.HasExtension("network_leases") {
		return nil, fmt.Errorf("The server is missing the required \"network_leases\" API extension")
	}

	leases := []api.NetworkLease{}

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

	return leases, nil
}

func
(
r
*
ProtocolLXD
)
GetNetworkLeases
(
name
string
)
(
[
]
api
.
NetworkLease
,
error
)
{
if
!
r
.
HasExtension
(
"
"
)
{
return
,
fmt
.
Errorf
(
"
\"
\"
"
)
}
leases
:=
[
]
api
.
NetworkLease
{
}
// Fetch the raw value
_
,
err
:=
r
.
queryStruct
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
name
)
)
,
,
"
"
,
&
leases
)
if
err
!=
{
return
,
err
}
return
leases
,
}

// GetNetworkLeases returns a list of Network struct


func (r *ProtocolLXD) GetNetworkState(name string) (*api.NetworkState, error) {
	if !r.HasExtension("network_state") {
		return nil, fmt.Errorf("The server is missing the required \"network_state\" API extension")
	}

	state := api.NetworkState{}

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

	return &state, nil
}

func
(
r
*
ProtocolLXD
)
GetNetworkState
(
name
string
)
(
*
api
.
NetworkState
,
error
)
{
if
!
r
.
HasExtension
(
"
"
)
{
return
,
fmt
.
Errorf
(
"
\"
\"
"
)
}
state
:=
api
.
NetworkState
{
}
// Fetch the raw value
_
,
err
:=
r
.
queryStruct
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
name
)
)
,
,
"
"
,
&
state
)
if
err
!=
{
return
,
err
}
return
&
state
,
}

// GetNetworkState returns metrics and information on the running network


func (r *ProtocolLXD) CreateNetwork(network api.NetworksPost) error {
	if !r.HasExtension("network") {
		return fmt.Errorf("The server is missing the required \"network\" API extension")
	}

	// Send the request
	_, _, err := r.query("POST", "/networks", network, "")
	if err != nil {
		return err
	}

	return nil
}

func
(
r
*
ProtocolLXD
)
CreateNetwork
(
network
api
.
NetworksPost
)
error
{
if
!
r
.
HasExtension
(
"
"
)
{
return
fmt
.
Errorf
(
"
\"
\"
"
)
}
// Send the request
_
,
_
,
err
:=
r
.
query
(
"
"
,
"
"
,
network
,
"
"
)
if
err
!=
{
return
err
}
return
}

// CreateNetwork defines a new network using the provided Network struct


func (r *ProtocolLXD) UpdateNetwork(name string, network api.NetworkPut, ETag string) error {
	if !r.HasExtension("network") {
		return fmt.Errorf("The server is missing the required \"network\" API extension")
	}

	// Send the request
	_, _, err := r.query("PUT", fmt.Sprintf("/networks/%s", url.QueryEscape(name)), network, ETag)
	if err != nil {
		return err
	}

	return nil
}

func
(
r
*
ProtocolLXD
)
UpdateNetwork
(
name
string
,
network
api
.
NetworkPut
,
ETag
string
)
error
{
if
!
r
.
HasExtension
(
"
"
)
{
return
fmt
.
Errorf
(
"
\"
\"
"
)
}
// Send the request
_
,
_
,
err
:=
r
.
query
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
name
)
)
,
network
,
ETag
)
if
err
!=
{
return
err
}
return
}

// UpdateNetwork updates the network to match the provided Network struct


func (r *ProtocolLXD) RenameNetwork(name string, network api.NetworkPost) error {
	if !r.HasExtension("network") {
		return fmt.Errorf("The server is missing the required \"network\" API extension")
	}

	// Send the request
	_, _, err := r.query("POST", fmt.Sprintf("/networks/%s", url.QueryEscape(name)), network, "")
	if err != nil {
		return err
	}

	return nil
}

func
(
r
*
ProtocolLXD
)
RenameNetwork
(
name
string
,
network
api
.
NetworkPost
)
error
{
if
!
r
.
HasExtension
(
"
"
)
{
return
fmt
.
Errorf
(
"
\"
\"
"
)
}
// Send the request
_
,
_
,
err
:=
r
.
query
(
"
"
,
fmt
.
Sprintf
(
"
"
,
url
.
QueryEscape
(
name
)
)
,
network
,
"
"
)
if
err
!=
{
return
err
}
return
}

// RenameNetwork renames an existing network entry


func Open(name string, store dqlite.ServerStore, options ...dqlite.DriverOption) (*sql.DB, error) {
	driver, err := dqlite.NewDriver(store, options...)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to create dqlite driver")
	}

	driverName := dqliteDriverName()
	sql.Register(driverName, driver)

	// Create the cluster db. This won't immediately establish any network
	// connection, that will happen only when a db transaction is started
	// (see the database/sql connection pooling code for more details).
	if name == "" {
		name = "db.bin"
	}
	db, err := sql.Open(driverName, name)
	if err != nil {
		return nil, fmt.Errorf("cannot open cluster database: %v", err)
	}

	return db, nil
}

// Open the cluster database object.
//
// The name argument is the name of the cluster database. It defaults to
// 'db.bin', but can be overwritten for testing.
//
// The dialer argument is a function that returns a gRPC dialer that can be
// used to connect to a database node using the gRPC SQL package.

Open the cluster database object . The name argument is the name of the cluster database . It defaults to db . bin but can be overwritten for testing . The dialer argument is a function that returns a gRPC dialer that can be used to connect to a database node using the gRPC SQL package .


func URLEncode(path string, query map[string]string) (string, error) {
	u, err := url.Parse(path)
	if err != nil {
		return "", err
	}

	params := url.Values{}
	for key, value := range query {
		params.Add(key, value)
	}
	u.RawQuery = params.Encode()
	return u.String(), nil
}

func URLEncode ( path string , query map [ string ] string ) ( string , error ) { u , err := url . Parse ( path ) if err != nil { return " " , err } params := url . Values { } for key , value := range query { params . Add ( key , value ) } u . RawQuery = params . Encode ( ) return u . String ( ) , nil }

// URLEncode encodes a path and query parameters to a URL.

URLEncode encodes a path and query parameters to a URL .


func IsUnixSocket(path string) bool {
	stat, err := os.Stat(path)
	if err != nil {
		return false
	}
	return (stat.Mode() & os.ModeSocket) == os.ModeSocket
}

func IsUnixSocket ( path string ) bool { stat , err := os . Stat ( path ) if err != nil { return false } return ( stat . Mode ( ) & os . ModeSocket ) == os . ModeSocket }

// IsUnixSocket returns true if the given path is either a Unix socket
// or a symbolic link pointing at a Unix socket.

IsUnixSocket returns true if the given path is either a Unix socket or a symbolic link pointing at a Unix socket .


func HostPath(path string) string {
	// Ignore empty paths
	if len(path) == 0 {
		return path
	}

	// Don't prefix stdin/stdout
	if path == "-" {
		return path
	}

	// Check if we're running in a snap package
	snap := os.Getenv("SNAP")
	snapName := os.Getenv("SNAP_NAME")
	if snap == "" || snapName != "lxd" {
		return path
	}

	// Handle relative paths
	if path[0] != os.PathSeparator {
		// Use the cwd of the parent as snap-confine alters our own cwd on launch
		ppid := os.Getppid()
		if ppid < 1 {
			return path
		}

		pwd, err := os.Readlink(fmt.Sprintf("/proc/%d/cwd", ppid))
		if err != nil {
			return path
		}

		path = filepath.Clean(strings.Join([]string{pwd, path}, string(os.PathSeparator)))
	}

	// Check if the path is already snap-aware
	for _, prefix := range []string{"/dev", "/snap", "/var/snap", "/var/lib/snapd"} {
		if path == prefix || strings.HasPrefix(path, fmt.Sprintf("%s/", prefix)) {
			return path
		}
	}

	return fmt.Sprintf("/var/lib/snapd/hostfs%s", path)
}

// HostPath returns the host path for the provided path
// On a normal system, this does nothing
// When inside of a snap environment, returns the real path

HostPath returns the host path for the provided path On a normal system this does nothing When inside of a snap environment returns the real path


func FileMove(oldPath string, newPath string) error {
	err := os.Rename(oldPath, newPath)
	if err == nil {
		return nil
	}

	err = FileCopy(oldPath, newPath)
	if err != nil {
		return err
	}

	os.Remove(oldPath)

	return nil
}

func FileMove ( oldPath string , newPath string ) error { err := os . Rename ( oldPath , newPath ) if err == nil { return nil } err = FileCopy ( oldPath , newPath ) if err != nil { return err } os . Remove ( oldPath ) return nil }

// FileMove tries to move a file by using os.Rename,
// if that fails it tries to copy the file and remove the source.

FileMove tries to move a file by using os . Rename if that fails it tries to copy the file and remove the source .


func DirCopy(source string, dest string) error {
	// Get info about source.
	info, err := os.Stat(source)
	if err != nil {
		return errors.Wrapf(err, "failed to get source directory info")
	}

	if !info.IsDir() {
		return fmt.Errorf("source is not a directory")
	}

	// Remove dest if it already exists.
	if PathExists(dest) {
		err := os.RemoveAll(dest)
		if err != nil {
			return errors.Wrapf(err, "failed to remove destination directory %s", dest)
		}
	}

	// Create dest.
	err = os.MkdirAll(dest, info.Mode())
	if err != nil {
		return errors.Wrapf(err, "failed to create destination directory %s", dest)
	}

	// Copy all files.
	entries, err := ioutil.ReadDir(source)
	if err != nil {
		return errors.Wrapf(err, "failed to read source directory %s", source)
	}

	for _, entry := range entries {

		sourcePath := filepath.Join(source, entry.Name())
		destPath := filepath.Join(dest, entry.Name())

		if entry.IsDir() {
			err := DirCopy(sourcePath, destPath)
			if err != nil {
				return errors.Wrapf(err, "failed to copy sub-directory from %s to %s", sourcePath, destPath)
			}
		} else {
			err := FileCopy(sourcePath, destPath)
			if err != nil {
				return errors.Wrapf(err, "failed to copy file from %s to %s", sourcePath, destPath)
			}
		}

	}

	return nil
}

func
DirCopy
(
source
string
,
dest
string
)
error
{
// Get info about source.
info
,
err
:=
os
.
Stat
(
source
)
if
err
!=
{
return
errors
.
Wrapf
(
err
,
"
"
)
}
if
!
info
.
IsDir
(
)
{
return
fmt
.
Errorf
(
"
"
)
}
// Remove dest if it already exists.
if
PathExists
(
dest
)
{
err
:=
os
.
RemoveAll
(
dest
)
if
err
!=
{
return
errors
.
Wrapf
(
err
,
"
"
,
dest
)
}
}
// Create dest.
err
=
os
.
MkdirAll
(
dest
,
info
.
Mode
(
)
)
if
err
!=
{
return
errors
.
Wrapf
(
err
,
"
"
,
dest
)
}
// Copy all files.
entries
,
err
:=
ioutil
.
ReadDir
(
source
)
if
err
!=
{
return
errors
.
Wrapf
(
err
,
"
"
,
source
)
}
for
_
,
entry
:=
range
entries
{
sourcePath
:=
filepath
.
Join
(
source
,
entry
.
Name
(
)
)
destPath
:=
filepath
.
Join
(
dest
,
entry
.
Name
(
)
)
if
entry
.
IsDir
(
)
{
err
:=
DirCopy
(
sourcePath
,
destPath
)
if
err
!=
{
return
errors
.
Wrapf
(
err
,
"
"
,
sourcePath
,
destPath
)
}
}
else
{
err
:=
FileCopy
(
sourcePath
,
destPath
)
if
err
!=
{
return
errors
.
Wrapf
(
err
,
"
"
,
sourcePath
,
destPath
)
}
}
}
return
}

// DirCopy copies a directory recursively, overwriting the target if it exists.

DirCopy copies a directory recursively overwriting the target if it exists .


func StringMapHasStringKey(m map[string]string, keys ...string) bool {
	for _, k := range keys {
		if _, ok := m[k]; ok {
			return true
		}
	}

	return false
}

func StringMapHasStringKey ( m map [ string ] string , keys ... string ) bool { for _ , k := range keys { if _ , ok := m [ k ] ; ok { return true } } return false }

// StringMapHasStringKey returns true if any of the supplied keys are present in the map.

StringMapHasStringKey returns true if any of the supplied keys are present in the map .


func TextEditor(inPath string, inContent []byte) ([]byte, error) {
	var f *os.File
	var err error
	var path string

	// Detect the text editor to use
	editor := os.Getenv("VISUAL")
	if editor == "" {
		editor = os.Getenv("EDITOR")
		if editor == "" {
			for _, p := range []string{"editor", "vi", "emacs", "nano"} {
				_, err := exec.LookPath(p)
				if err == nil {
					editor = p
					break
				}
			}
			if editor == "" {
				return []byte{}, fmt.Errorf("No text editor found, please set the EDITOR environment variable")
			}
		}
	}

	if inPath == "" {
		// If provided input, create a new file
		f, err = ioutil.TempFile("", "lxd_editor_")
		if err != nil {
			return []byte{}, err
		}

		err = os.Chmod(f.Name(), 0600)
		if err != nil {
			f.Close()
			os.Remove(f.Name())
			return []byte{}, err
		}

		f.Write(inContent)
		f.Close()

		path = fmt.Sprintf("%s.yaml", f.Name())
		os.Rename(f.Name(), path)
		defer os.Remove(path)
	} else {
		path = inPath
	}

	cmdParts := strings.Fields(editor)
	cmd := exec.Command(cmdParts[0], append(cmdParts[1:], path)...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	if err != nil {
		return []byte{}, err
	}

	content, err := ioutil.ReadFile(path)
	if err != nil {
		return []byte{}, err
	}

	return content, nil
}

func
TextEditor
(
inPath
string
,
inContent
[
]
byte
)
(
[
]
byte
,
error
)
{
var
f
*
os
.
File
var
err
error
var
path
string
// Detect the text editor to use
editor
:=
os
.
Getenv
(
"
"
)
if
editor
==
"
"
{
editor
=
os
.
Getenv
(
"
"
)
if
editor
==
"
"
{
for
_
,
p
:=
range
[
]
string
{
"
"
,
"
"
,
"
"
,
"
"
}
{
_
,
err
:=
exec
.
LookPath
(
p
)
if
err
==
{
editor
=
p
break
}
}
if
editor
==
"
"
{
return
[
]
byte
{
}
,
fmt
.
Errorf
(
"
"
)
}
}
}
if
inPath
==
"
"
{
// If provided input, create a new file
f
,
err
=
ioutil
.
TempFile
(
"
"
,
"
"
)
if
err
!=
{
return
[
]
byte
{
}
,
err
}
err
=
os
.
Chmod
(
f
.
Name
(
)
,
0600
)
if
err
!=
{
f
.
Close
(
)
os
.
Remove
(
f
.
Name
(
)
)
return
[
]
byte
{
}
,
err
}
f
.
Write
(
inContent
)
f
.
Close
(
)
path
=
fmt
.
Sprintf
(
"
"
,
f
.
Name
(
)
)
os
.
Rename
(
f
.
Name
(
)
,
path
)
defer
os
.
Remove
(
path
)
}
else
{
path
=
inPath
}
cmdParts
:=
strings
.
Fields
(
editor
)
cmd
:=
exec
.
Command
(
cmdParts
[
0
]
,
append
(
cmdParts
[
1
:
]
,
path
)
...
)
cmd
.
Stdin
=
os
.
Stdin
cmd
.
Stdout
=
os
.
Stdout
cmd
.
Stderr
=
os
.
Stderr
err
=
cmd
.
Run
(
)
if
err
!=
{
return
[
]
byte
{
}
,
err
}
content
,
err
:=
ioutil
.
ReadFile
(
path
)
if
err
!=
{
return
[
]
byte
{
}
,
err
}
return
content
,
}

// Spawn the editor with a temporary YAML file for editing configs


func WriteTempFile(dir string, prefix string, content string) (string, error) {
	f, err := ioutil.TempFile(dir, prefix)
	if err != nil {
		return "", err
	}
	defer f.Close()

	_, err = f.WriteString(content)
	return f.Name(), err
}

func WriteTempFile ( dir string , prefix string , content string ) ( string , error ) { f , err := ioutil . TempFile ( dir , prefix ) if err != nil { return " " , err } defer f . Close ( ) _ , err = f . WriteString ( content ) return f . Name ( ) , err }

// WriteTempFile creates a temp file with the specified content


func RenderTemplate(template string, ctx pongo2.Context) (string, error) {
	// Load template from string
	tpl, err := pongo2.FromString("{% autoescape off %}" + template + "{% endautoescape %}")
	if err != nil {
		return "", err
	}

	// Get rendered template
	ret, err := tpl.Execute(ctx)
	if err != nil {
		return ret, err
	}

	// Looks like we're nesting templates so run pongo again
	if strings.Contains(ret, "{{") || strings.Contains(ret, "{%") {
		return RenderTemplate(ret, ctx)
	}

	return ret, err
}

// RenderTemplate renders a pongo2 template.

RenderTemplate renders a pongo2 template .


func Every(interval time.Duration, options ...EveryOption) Schedule {
	every := &every{}
	for _, option := range options {
		option(every)
	}
	first := true
	return func() (time.Duration, error) {
		var err error
		if first && every.skipFirst {
			err = ErrSkip
		}
		first = false
		return interval, err
	}
}

func Every ( interval time . Duration , options ... EveryOption ) Schedule { every := & every { } for _ , option := range options { option ( every ) } first := true return func ( ) ( time . Duration , error ) { var err error if first && every . skipFirst { err = ErrSkip } first = false return interval , err } }

// Every returns a Schedule that always returns the given time interval.

Every returns a Schedule that always returns the given time interval .


func (s *storageLvm) StoragePoolMount() (bool, error) {
	source := s.pool.Config["source"]
	if source == "" {
		return false, fmt.Errorf("no \"source\" property found for the storage pool")
	}

	if !filepath.IsAbs(source) {
		return true, nil
	}

	poolMountLockID := getPoolMountLockID(s.pool.Name)
	lxdStorageMapLock.Lock()
	if waitChannel, ok := lxdStorageOngoingOperationMap[poolMountLockID]; ok {
		lxdStorageMapLock.Unlock()
		if _, ok := <-waitChannel; ok {
			logger.Warnf("Received value over semaphore, this should not have happened")
		}
		// Give the benefit of the doubt and assume that the other
		// thread actually succeeded in mounting the storage pool.
		return false, nil
	}

	lxdStorageOngoingOperationMap[poolMountLockID] = make(chan bool)
	lxdStorageMapLock.Unlock()

	removeLockFromMap := func() {
		lxdStorageMapLock.Lock()
		if waitChannel, ok := lxdStorageOngoingOperationMap[poolMountLockID]; ok {
			close(waitChannel)
			delete(lxdStorageOngoingOperationMap, poolMountLockID)
		}
		lxdStorageMapLock.Unlock()
	}

	defer removeLockFromMap()

	if filepath.IsAbs(source) && !shared.IsBlockdevPath(source) {
		// Try to prepare new loop device.
		loopF, loopErr := prepareLoopDev(source, 0)
		if loopErr != nil {
			return false, loopErr
		}
		// Make sure that LO_FLAGS_AUTOCLEAR is unset.
		loopErr = unsetAutoclearOnLoopDev(int(loopF.Fd()))
		if loopErr != nil {
			return false, loopErr
		}
		s.loopInfo = loopF
	}

	return true, nil
}

// Currently only used for loop-backed LVM storage pools. Can be called without
// overhead since it is essentially a noop for non-loop-backed LVM storage
// pools.

Currently only used for loop - backed LVM storage pools . Can be called without overhead since it is essentially a noop for non - loop - backed LVM storage pools .


func Dump(tx *sql.Tx, schema string, schemaOnly bool) (string, error) {
	schemas := dumpParseSchema(schema)

	// Begin
	dump := `PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
`
	// Schema table
	tableDump, err := dumpTable(tx, "schema", dumpSchemaTable)
	if err != nil {
		return "", errors.Wrapf(err, "failed to dump table schema")
	}
	dump += tableDump

	// All other tables
	tables := make([]string, 0)
	for table := range schemas {
		tables = append(tables, table)
	}
	sort.Strings(tables)
	for _, table := range tables {
		if schemaOnly {
			// Dump only the schema.
			dump += schemas[table] + "\n"
			continue
		}
		tableDump, err := dumpTable(tx, table, schemas[table])
		if err != nil {
			return "", errors.Wrapf(err, "failed to dump table %s", table)
		}
		dump += tableDump
	}

	// Sequences (unless the schemaOnly flag is true)
	if !schemaOnly {
		tableDump, err = dumpTable(tx, "sqlite_sequence", "DELETE FROM sqlite_sequence;")
		if err != nil {
			return "", errors.Wrapf(err, "failed to dump table sqlite_sequence")
		}
		dump += tableDump
	}

	// Commit
	dump += "COMMIT;\n"

	return dump, nil
}

// Dump returns a SQL text dump of all rows across all tables, similar to
// sqlite3's dump feature

Dump returns a SQL text dump of all rows across all tables similar to sqlite3 s dump feature


func dumpTable(tx *sql.Tx, table, schema string) (string, error) {
	statements := []string{schema}

	// Query all rows.
	rows, err := tx.Query(fmt.Sprintf("SELECT * FROM %s ORDER BY rowid", table))
	if err != nil {
		return "", errors.Wrap(err, "failed to fetch rows")
	}
	defer rows.Close()

	// Figure column names
	columns, err := rows.Columns()
	if err != nil {
		return "", errors.Wrap(err, "failed to get columns")
	}

	// Generate an INSERT statement for each row.
	for i := 0; rows.Next(); i++ {
		raw := make([]interface{}, len(columns)) // Raw column values
		row := make([]interface{}, len(columns))
		for i := range raw {
			row[i] = &raw[i]
		}
		err := rows.Scan(row...)
		if err != nil {
			return "", errors.Wrapf(err, "failed to scan row %d", i)
		}
		values := make([]string, len(columns))
		for j, v := range raw {
			switch v := v.(type) {
			case int64:
				values[j] = strconv.FormatInt(v, 10)
			case string:
				values[j] = fmt.Sprintf("'%s'", v)
			case []byte:
				values[j] = fmt.Sprintf("'%s'", string(v))
			case time.Time:
				values[j] = strconv.FormatInt(v.Unix(), 10)
			default:
				if v != nil {
					return "", fmt.Errorf("bad type in column %s of row %d", columns[j], i)
				}
				values[j] = "NULL"
			}
		}
		statement := fmt.Sprintf("INSERT INTO %s VALUES(%s);", table, strings.Join(values, ","))
		statements = append(statements, statement)
	}
	return strings.Join(statements, "\n") + "\n", nil
}

// Dump a single table, returning a SQL text containing statements for its
// schema and data.

Dump a single table returning a SQL text containing statements for its schema and data .


func (c *ClusterTx) ProjectHasProfiles(name string) (bool, error) {
	return projectHasProfiles(c.tx, name)
}

func ( c * ClusterTx ) ProjectHasProfiles ( name string ) ( bool , error ) { return projectHasProfiles ( c . tx , name ) }

// ProjectHasProfiles is a helper to check if a project has the profiles
// feature enabled.

ProjectHasProfiles is a helper to check if a project has the profiles feature enabled .


func (c *ClusterTx) ProjectNames() ([]string, error) {
	stmt := "SELECT name FROM projects"

	names, err := query.SelectStrings(c.tx, stmt)
	if err != nil {
		return nil, errors.Wrap(err, "Fetch project names")
	}

	return names, nil
}

func ( c * ClusterTx ) ProjectNames ( ) ( [ ] string , error ) { stmt := " " names , err := query . SelectStrings ( c . tx , stmt ) if err != nil { return nil , errors . Wrap ( err , " " ) } return names , nil }

// ProjectNames returns the names of all available projects.

ProjectNames returns the names of all available projects .


func (c *ClusterTx) ProjectMap() (map[int64]string, error) {
	stmt := "SELECT id, name FROM projects"

	rows, err := c.tx.Query(stmt)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	result := map[int64]string{}
	for i := 0; rows.Next(); i++ {
		var id int64
		var name string

		err := rows.Scan(&id, &name)
		if err != nil {
			return nil, err
		}

		result[id] = name
	}

	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return result, nil
}

func ( c * ClusterTx ) ProjectMap ( ) ( map [ int64 ] string , error ) { stmt := " " rows , err := c . tx . Query ( stmt ) if err != nil { return nil , err } defer rows . Close ( ) result := map [ int64 ] string { } for i := 0 ; rows . Next ( ) ; i ++ { var id int64 var name string err := rows . Scan ( & id , & name ) if err != nil { return nil , err } result [ id ] = name } err = rows . Err ( ) if err != nil { return nil , err } return result , nil }

// ProjectMap returns the names and ids of all available projects.

ProjectMap returns the names and ids of all available projects .


func (c *ClusterTx) ProjectHasImages(name string) (bool, error) {
	project, err := c.ProjectGet(name)
	if err != nil {
		return false, errors.Wrap(err, "fetch project")
	}

	enabled := project.Config["features.images"] == "true"

	return enabled, nil
}

func ( c * ClusterTx ) ProjectHasImages ( name string ) ( bool , error ) { project , err := c . ProjectGet ( name ) if err != nil { return false , errors . Wrap ( err , " " ) } enabled := project . Config [ " " ] == " " return enabled , nil }

// ProjectHasImages is a helper to check if a project has the images
// feature enabled.

ProjectHasImages is a helper to check if a project has the images feature enabled .


func (c *ClusterTx) ProjectUpdate(name string, object api.ProjectPut) error {
	stmt := c.stmt(projectUpdate)
	result, err := stmt.Exec(object.Description, name)
	if err != nil {
		return errors.Wrap(err, "Update project")
	}

	n, err := result.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "Fetch affected rows")
	}
	if n != 1 {
		return fmt.Errorf("Query updated %d rows instead of 1", n)
	}

	id, err := c.ProjectID(name)
	if err != nil {
		return errors.Wrap(err, "Fetch project ID")
	}

	// Clear config.
	_, err = c.tx.Exec(`
DELETE FROM projects_config WHERE projects_config.project_id = ?
`, id)
	if err != nil {
		return errors.Wrap(err, "Delete project config")
	}

	// Insert new config.
	stmt = c.stmt(projectCreateConfigRef)
	for key, value := range object.Config {
		_, err := stmt.Exec(id, key, value)
		if err != nil {
			return errors.Wrap(err, "Insert config for project")
		}
	}

	return nil
}

// ProjectUpdate updates the project matching the given key parameters.

ProjectUpdate updates the project matching the given key parameters .


func (r *ProtocolLXD) GetCluster() (*api.Cluster, string, error) {
	if !r.HasExtension("clustering") {
		return nil, "", fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	cluster := &api.Cluster{}
	etag, err := r.queryStruct("GET", "/cluster", nil, "", &cluster)
	if err != nil {
		return nil, "", err
	}

	return cluster, etag, nil
}

func ( r * ProtocolLXD ) GetCluster ( ) ( * api . Cluster , string , error ) { if ! r . HasExtension ( " " ) { return nil , " " , fmt . Errorf ( " \" \" " ) } cluster := & api . Cluster { } etag , err := r . queryStruct ( " " , " " , nil , " " , & cluster ) if err != nil { return nil , " " , err } return cluster , etag , nil }

// GetCluster returns information about a cluster
//
// If this client is not trusted, the password must be supplied

GetCluster returns information about a cluster If this client is not trusted the password must be supplied


func (r *ProtocolLXD) UpdateCluster(cluster api.ClusterPut, ETag string) (Operation, error) {
	if !r.HasExtension("clustering") {
		return nil, fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	if cluster.ServerAddress != "" || cluster.ClusterPassword != "" || len(cluster.MemberConfig) > 0 {
		if !r.HasExtension("clustering_join") {
			return nil, fmt.Errorf("The server is missing the required \"clustering_join\" API extension")
		}
	}

	op, _, err := r.queryOperation("PUT", "/cluster", cluster, "")
	if err != nil {
		return nil, err
	}

	return op, nil
}

func ( r * ProtocolLXD ) UpdateCluster ( cluster api . ClusterPut , ETag string ) ( Operation , error ) { if ! r . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } if cluster . ServerAddress != " " || cluster . ClusterPassword != " " || len ( cluster . MemberConfig ) > 0 { if ! r . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } } op , _ , err := r . queryOperation ( " " , " " , cluster , " " ) if err != nil { return nil , err } return op , nil }

// UpdateCluster requests to bootstrap a new cluster or join an existing one.

UpdateCluster requests to bootstrap a new cluster or join an existing one .


func (r *ProtocolLXD) GetClusterMemberNames() ([]string, error) {
	if !r.HasExtension("clustering") {
		return nil, fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	urls := []string{}
	_, err := r.queryStruct("GET", "/cluster/members", nil, "", &urls)
	if err != nil {
		return nil, err
	}

	return urls, nil
}

func ( r * ProtocolLXD ) GetClusterMemberNames ( ) ( [ ] string , error ) { if ! r . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } urls := [ ] string { } _ , err := r . queryStruct ( " " , " " , nil , " " , & urls ) if err != nil { return nil , err } return urls , nil }

// GetClusterMemberNames returns the URLs of the current members in the cluster


func (r *ProtocolLXD) GetClusterMembers() ([]api.ClusterMember, error) {
	if !r.HasExtension("clustering") {
		return nil, fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	members := []api.ClusterMember{}
	_, err := r.queryStruct("GET", "/cluster/members?recursion=1", nil, "", &members)
	if err != nil {
		return nil, err
	}

	return members, nil
}

func ( r * ProtocolLXD ) GetClusterMembers ( ) ( [ ] api . ClusterMember , error ) { if ! r . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } members := [ ] api . ClusterMember { } _ , err := r . queryStruct ( " " , " " , nil , " " , & members ) if err != nil { return nil , err } return members , nil }

// GetClusterMembers returns the current members of the cluster


func (r *ProtocolLXD) GetClusterMember(name string) (*api.ClusterMember, string, error) {
	if !r.HasExtension("clustering") {
		return nil, "", fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	member := api.ClusterMember{}
	etag, err := r.queryStruct("GET", fmt.Sprintf("/cluster/members/%s", name), nil, "", &member)
	if err != nil {
		return nil, "", err
	}

	return &member, etag, nil
}

func ( r * ProtocolLXD ) GetClusterMember ( name string ) ( * api . ClusterMember , string , error ) { if ! r . HasExtension ( " " ) { return nil , " " , fmt . Errorf ( " \" \" " ) } member := api . ClusterMember { } etag , err := r . queryStruct ( " " , fmt . Sprintf ( " " , name ) , nil , " " , & member ) if err != nil { return nil , " " , err } return & member , etag , nil }

// GetClusterMember returns information about the given member


func (r *ProtocolLXD) RenameClusterMember(name string, member api.ClusterMemberPost) error {
	if !r.HasExtension("clustering") {
		return fmt.Errorf("The server is missing the required \"clustering\" API extension")
	}

	_, _, err := r.query("POST", fmt.Sprintf("/cluster/members/%s", name), member, "")
	if err != nil {
		return err
	}

	return nil
}

func ( r * ProtocolLXD ) RenameClusterMember ( name string , member api . ClusterMemberPost ) error { if ! r . HasExtension ( " " ) { return fmt . Errorf ( " \" \" " ) } _ , _ , err := r . query ( " " , fmt . Sprintf ( " " , name ) , member , " " ) if err != nil { return err } return nil }

// RenameClusterMember changes the name of an existing member


func (e *EventListener) Disconnect() {
	if e.disconnected {
		return
	}

	// Handle locking
	e.r.eventListenersLock.Lock()
	defer e.r.eventListenersLock.Unlock()

	// Locate and remove it from the global list
	for i, listener := range e.r.eventListeners {
		if listener == e {
			copy(e.r.eventListeners[i:], e.r.eventListeners[i+1:])
			e.r.eventListeners[len(e.r.eventListeners)-1] = nil
			e.r.eventListeners = e.r.eventListeners[:len(e.r.eventListeners)-1]
			break
		}
	}

	// Turn off the handler
	e.err = nil
	e.disconnected = true
	close(e.chActive)
}

// Disconnect must be used once done listening for events


func CompareVersions(version1, version2 [2]int) (int, error) {
	schema1, extensions1 := version1[0], version1[1]
	schema2, extensions2 := version2[0], version2[1]

	if schema1 == schema2 && extensions1 == extensions2 {
		return 0, nil
	}
	if schema1 >= schema2 && extensions1 >= extensions2 {
		return 1, nil
	}
	if schema1 <= schema2 && extensions1 <= extensions2 {
		return 2, nil
	}

	return -1, fmt.Errorf("nodes have inconsistent versions")
}

func CompareVersions ( version1 , version2 [ 2 ] int ) ( int , error ) { schema1 , extensions1 := version1 [ 0 ] , version1 [ 1 ] schema2 , extensions2 := version2 [ 0 ] , version2 [ 1 ] if schema1 == schema2 && extensions1 == extensions2 { return 0 , nil } if schema1 >= schema2 && extensions1 >= extensions2 { return 1 , nil } if schema1 <= schema2 && extensions1 <= extensions2 { return 2 , nil } return - 1 , fmt . Errorf ( " " ) }

// CompareVersions the versions of two LXD nodes.
//
// A version consists of the version the node's schema and the number of API
// extensions it supports.
//
// Return 0 if they equal, 1 if the first version is greater than the second
// and 2 if the second is greater than the first.
//
// Return an error if inconsistent versions are detected, for example the first
// node's schema is greater than the second's, but the number of extensions is
// smaller.

CompareVersions the versions of two LXD nodes . A version consists of the version the node s schema and the number of API extensions it supports . Return 0 if they equal 1 if the first version is greater than the second and 2 if the second is greater than the first . Return an error if inconsistent versions are detected for example the first node s schema is greater than the second s but the number of extensions is smaller .


func (c *Config) HasClientCertificate() bool {
	certf := c.ConfigPath("client.crt")
	keyf := c.ConfigPath("client.key")
	if !shared.PathExists(certf) || !shared.PathExists(keyf) {
		return false
	}

	return true
}

func ( c * Config ) HasClientCertificate ( ) bool { certf := c . ConfigPath ( " " ) keyf := c . ConfigPath ( " " ) if ! shared . PathExists ( certf ) || ! shared . PathExists ( keyf ) { return false } return true }

// HasClientCertificate will return true if a client certificate has already been generated


func (c *Config) GenerateClientCertificate() error {
	if c.HasClientCertificate() {
		return nil
	}

	certf := c.ConfigPath("client.crt")
	keyf := c.ConfigPath("client.key")

	return shared.FindOrGenCert(certf, keyf, true)
}

func ( c * Config ) GenerateClientCertificate ( ) error { if c . HasClientCertificate ( ) { return nil } certf := c . ConfigPath ( " " ) keyf := c . ConfigPath ( " " ) return shared . FindOrGenCert ( certf , keyf , true ) }

// GenerateClientCertificate will generate the needed client.crt and client.key if needed

GenerateClientCertificate will generate the needed client . crt and client . key if needed


func LoadModule(module string) error {
	if shared.PathExists(fmt.Sprintf("/sys/module/%s", module)) {
		return nil
	}

	_, err := shared.RunCommand("modprobe", module)
	return err
}

func LoadModule ( module string ) error { if shared . PathExists ( fmt . Sprintf ( " " , module ) ) { return nil } _ , err := shared . RunCommand ( " " , module ) return err }

// LoadModule loads the kernel module with the given name, by invoking
// modprobe.

LoadModule loads the kernel module with the given name by invoking modprobe .


func Parse(name string) (*ast.Package, error) {
	base := os.Getenv("GOPATH")
	if base == "" {
		base = "~/go"
	}
	dir := filepath.Join(base, "src", name)

	fset := token.NewFileSet()

	paths, err := filepath.Glob(filepath.Join(dir, "*.go"))
	if err != nil {
		return nil, errors.Wrap(err, "Search source file")
	}

	files := map[string]*ast.File{}
	for _, path := range paths {
		// Skip test files.
		if strings.Contains(path, "_test.go") {
			continue
		}

		file, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
		if err != nil {
			return nil, fmt.Errorf("Parse Go source file %q", path)
		}

		files[path] = file
	}

	// Ignore errors because they are typically about unresolved symbols.
	pkg, _ := ast.NewPackage(fset, files, nil, nil)

	return pkg, nil
}

// Parse runs the Go parser against the given package name.

Parse runs the Go parser against the given package name .


func (e *Endpoints) PprofAddress() string {
	e.mu.RLock()
	defer e.mu.RUnlock()

	listener := e.listeners[pprof]
	if listener == nil {
		return ""
	}

	return listener.Addr().String()
}

func ( e * Endpoints ) PprofAddress ( ) string { e . mu . RLock ( ) defer e . mu . RUnlock ( ) listener := e . listeners [ pprof ] if listener == nil { return " " } return listener . Addr ( ) . String ( ) }

// PprofAddress returns the network addresss of the pprof endpoint, or an empty string if there's no pprof endpoint

PprofAddress returns the network addresss of the pprof endpoint or an empty string if there s no pprof endpoint


func (e *Endpoints) PprofUpdateAddress(address string) error {
	if address != "" {
		address = util.CanonicalNetworkAddress(address)
	}

	oldAddress := e.NetworkAddress()
	if address == oldAddress {
		return nil
	}

	logger.Infof("Update pprof address")

	e.mu.Lock()
	defer e.mu.Unlock()

	// Close the previous socket
	e.closeListener(pprof)

	// If turning off listening, we're done
	if address == "" {
		return nil
	}

	// Attempt to setup the new listening socket
	getListener := func(address string) (*net.Listener, error) {
		var err error
		var listener net.Listener

		for i := 0; i < 10; i++ { // Ten retries over a second seems reasonable.
			listener, err = net.Listen("tcp", address)
			if err == nil {
				break
			}

			time.Sleep(100 * time.Millisecond)
		}

		if err != nil {
			return nil, fmt.Errorf("Cannot listen on http socket: %v", err)
		}

		return &listener, nil
	}

	// If setting a new address, setup the listener
	if address != "" {
		listener, err := getListener(address)
		if err != nil {
			// Attempt to revert to the previous address
			listener, err1 := getListener(oldAddress)
			if err1 == nil {
				e.listeners[pprof] = *listener
				e.serveHTTP(pprof)
			}

			return err
		}

		e.listeners[pprof] = *listener
		e.serveHTTP(pprof)
	}

	return nil
}

// PprofUpdateAddress updates the address for the pprof endpoint, shutting it down and restarting it.

PprofUpdateAddress updates the address for the pprof endpoint shutting it down and restarting it .


func NewMethod(database, pkg, entity, kind string, config map[string]string) (*Method, error) {
	packages, err := Packages()
	if err != nil {
		return nil, err
	}

	method := &Method{
		db:       database,
		pkg:      pkg,
		entity:   entity,
		kind:     kind,
		config:   config,
		packages: packages,
	}

	return method, nil
}

func NewMethod ( database , pkg , entity , kind string , config map [ string ] string ) ( * Method , error ) { packages , err := Packages ( ) if err != nil { return nil , err } method := & Method { db : database , pkg : pkg , entity : entity , kind : kind , config : config , packages : packages , } return method , nil }

// NewMethod return a new method code snippet for executing a certain mapping.

NewMethod return a new method code snippet for executing a certain mapping .


func (m *Method) Generate(buf *file.Buffer) error {
	if strings.HasSuffix(m.kind, "Ref") {
		return m.ref(buf)
	}
	switch m.kind {
	case "URIs":
		return m.uris(buf)
	case "List":
		return m.list(buf)
	case "Get":
		return m.get(buf)
	case "ID":
		return m.id(buf)
	case "Exists":
		return m.exists(buf)
	case "Create":
		return m.create(buf)
	case "Rename":
		return m.rename(buf)
	case "Update":
		return m.update(buf)
	case "Delete":
		return m.delete(buf)
	default:
		return fmt.Errorf("Unknown method kind '%s'", m.kind)
	}
}

func ( m * Method ) Generate ( buf * file . Buffer ) error { if strings . HasSuffix ( m . kind , " " ) { return m . ref ( buf ) } switch m . kind { case " " : return m . uris ( buf ) case " " : return m . list ( buf ) case " " : return m . get ( buf ) case " " : return m . id ( buf ) case " " : return m . exists ( buf ) case " " : return m . create ( buf ) case " " : return m . rename ( buf ) case " " : return m . update ( buf ) case " " : return m . delete ( buf ) default : return fmt . Errorf ( " " , m . kind ) } }


func (m *Method) fillSliceReferenceField(buf *file.Buffer, nk []*Field, field *Field) error {
	objectsVar := fmt.Sprintf("%sObjects", lex.Minuscule(field.Name))
	methodName := fmt.Sprintf("%s%sRef", lex.Capital(m.entity), field.Name)

	buf.L("// Fill field %s.", field.Name)
	buf.L("%s, err := c.%s(filter)", objectsVar, methodName)
	buf.L("if err != nil {")
	buf.L("        return nil, errors.Wrap(err, \"Failed to fetch field %s\")", field.Name)
	buf.L("}")
	buf.N()
	buf.L("for i := range objects {")
	needle := ""
	for i, key := range nk[:len(nk)-1] {
		needle += fmt.Sprintf("[objects[i].%s]", key.Name)
		subIndexTyp := indexType(nk[i+1:], field.Type.Name)
		buf.L("        _, ok := %s%s", objectsVar, needle)
		buf.L("        if !ok {")
		buf.L("                subIndex := %s{}", subIndexTyp)
		buf.L("                %s%s = subIndex", objectsVar, needle)
		buf.L("        }")
		buf.N()
	}

	needle += fmt.Sprintf("[objects[i].%s]", nk[len(nk)-1].Name)
	buf.L("        value := %s%s", objectsVar, needle)
	buf.L("        if value == nil {")
	buf.L("                value = %s{}", field.Type.Name)
	buf.L("        }")
	buf.L("        objects[i].%s = value", field.Name)
	buf.L("}")
	buf.N()

	return nil
}

func ( m * Method ) fillSliceReferenceField ( buf * file . Buffer , nk [ ] * Field , field * Field ) error { objectsVar := fmt . Sprintf ( " " , lex . Minuscule ( field . Name ) ) methodName := fmt . Sprintf ( " " , lex . Capital ( m . entity ) , field . Name ) buf . L ( " " , field . Name ) buf . L ( " " , objectsVar , methodName ) buf . L ( " " ) buf . L ( " \" \" " , field . Name ) buf . L ( " " ) buf . N ( ) buf . L ( " " ) needle := " " for i , key := range nk [ : len ( nk ) - 1 ] { needle += fmt . Sprintf ( " " , key . Name ) subIndexTyp := indexType ( nk [ i + 1 : ] , field . Type . Name ) buf . L ( " " , objectsVar , needle ) buf . L ( " " ) buf . L ( " " , subIndexTyp ) buf . L ( " " , objectsVar , needle ) buf . L ( " " ) buf . N ( ) } needle += fmt . Sprintf ( " " , nk [ len ( nk ) - 1 ] . Name ) buf . L ( " " , objectsVar , needle ) buf . L ( " " ) buf . L ( " " , field . Type . Name ) buf . L ( " " ) buf . L ( " " , field . Name ) buf . L ( " " ) buf . N ( ) return nil }

// Populate a field consisting of a slice of objects referencing the
// entity. This information is available by joining a the view or table
// associated with the type of the referenced objects, which must contain the
// natural key of the entity.

Populate a field consisting of a slice of objects referencing the entity . This information is available by joining a the view or table associated with the type of the referenced objects which must contain the natural key of the entity .


func (c *ClusterTx) StoragePoolID(name string) (int64, error) {
	stmt := "SELECT id FROM storage_pools WHERE name=?"
	ids, err := query.SelectIntegers(c.tx, stmt, name)
	if err != nil {
		return -1, err
	}
	switch len(ids) {
	case 0:
		return -1, ErrNoSuchObject
	case 1:
		return int64(ids[0]), nil
	default:
		return -1, fmt.Errorf("more than one pool has the given name")
	}
}

func ( c * ClusterTx ) StoragePoolID ( name string ) ( int64 , error ) { stmt := " " ids , err := query . SelectIntegers ( c . tx , stmt , name ) if err != nil { return - 1 , err } switch len ( ids ) { case 0 : return - 1 , ErrNoSuchObject case 1 : return int64 ( ids [ 0 ] ) , nil default : return - 1 , fmt . Errorf ( " " ) } }

// StoragePoolID returns the ID of the pool with the given name.

StoragePoolID returns the ID of the pool with the given name .


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

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

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

// StoragePoolIDsNotPending returns a map associating each storage pool name to its ID.
//
// Pending storage pools are skipped.

StoragePoolIDsNotPending returns a map associating each storage pool name to its ID . Pending storage pools are skipped .


func (c *ClusterTx) StoragePoolNodeJoin(poolID, nodeID int64) error {
	columns := []string{"storage_pool_id", "node_id"}
	values := []interface{}{poolID, nodeID}
	_, err := query.UpsertObject(c.tx, "storage_pools_nodes", columns, values)
	if err != nil {
		return errors.Wrap(err, "failed to add storage pools node entry")
	}

	return nil
}

func ( c * ClusterTx ) StoragePoolNodeJoin ( poolID , nodeID int64 ) error { columns := [ ] string { " " , " " } values := [ ] interface { } { poolID , nodeID } _ , err := query . UpsertObject ( c . tx , " " , columns , values ) if err != nil { return errors . Wrap ( err , " " ) } return nil }

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

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


func (c *ClusterTx) StoragePoolNodeJoinCeph(poolID, nodeID int64) error {
	// Get the IDs of the other nodes (they should be all linked to
	// the pool).
	stmt := "SELECT node_id FROM storage_pools_nodes WHERE storage_pool_id=?"
	nodeIDs, err := query.SelectIntegers(c.tx, stmt, poolID)
	if err != nil {
		return errors.Wrap(err, "failed to fetch IDs of nodes with ceph pool")
	}
	if len(nodeIDs) == 0 {
		return fmt.Errorf("ceph pool is not linked to any node")
	}
	otherNodeID := nodeIDs[0]

	// Create entries of all the ceph volumes for the new node.
	_, err = c.tx.Exec(`
INSERT INTO storage_volumes(name, storage_pool_id, node_id, type, description, project_id)
  SELECT name, storage_pool_id, ?, type, description, 1
    FROM storage_volumes WHERE storage_pool_id=? AND node_id=?
`, nodeID, poolID, otherNodeID)
	if err != nil {
		return errors.Wrap(err, "failed to create node ceph volumes")
	}

	// Create entries of all the ceph volumes configs for the new node.
	stmt = `
SELECT id FROM storage_volumes WHERE storage_pool_id=? AND node_id=?
  ORDER BY name, type
`
	volumeIDs, err := query.SelectIntegers(c.tx, stmt, poolID, nodeID)
	if err != nil {
		return errors.Wrap(err, "failed to get joining node's ceph volume IDs")
	}
	otherVolumeIDs, err := query.SelectIntegers(c.tx, stmt, poolID, otherNodeID)
	if err != nil {
		return errors.Wrap(err, "failed to get other node's ceph volume IDs")
	}
	if len(volumeIDs) != len(otherVolumeIDs) { // Sanity check
		return fmt.Errorf("not all ceph volumes were copied")
	}
	for i, otherVolumeID := range otherVolumeIDs {
		config, err := query.SelectConfig(
			c.tx, "storage_volumes_config", "storage_volume_id=?", otherVolumeID)
		if err != nil {
			return errors.Wrap(err, "failed to get storage volume config")
		}
		for key, value := range config {
			_, err := c.tx.Exec(`
INSERT INTO storage_volumes_config(storage_volume_id, key, value) VALUES(?, ?, ?)
`, volumeIDs[i], key, value)
			if err != nil {
				return errors.Wrap(err, "failed to copy volume config")
			}
		}
	}

	return nil
}

// StoragePoolNodeJoinCeph updates internal state to reflect that nodeID is
// joining a cluster where poolID is a ceph pool.

StoragePoolNodeJoinCeph updates internal state to reflect that nodeID is joining a cluster where poolID is a ceph pool .


func (c *ClusterTx) StoragePoolConfigAdd(poolID, nodeID int64, config map[string]string) error {
	return storagePoolConfigAdd(c.tx, poolID, nodeID, config)
}

func ( c * ClusterTx ) StoragePoolConfigAdd ( poolID , nodeID int64 , config map [ string ] string ) error { return storagePoolConfigAdd ( c . tx , poolID , nodeID , config ) }

// StoragePoolConfigAdd adds a new entry in the storage_pools_config table


func (c *ClusterTx) StoragePoolCreatePending(node, name, driver string, conf map[string]string) error {
	// First check if a storage pool with the given name exists, and, if
	// so, that it has a matching driver and it's in the pending state.
	pool := struct {
		id     int64
		driver string
		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 pool exists with the given name")
		}
		return []interface{}{&pool.id, &pool.driver, &pool.state}
	}
	stmt, err := c.tx.Prepare("SELECT id, driver, state FROM storage_pools 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 poolID = pool.id
	if poolID == 0 {
		// No existing pool with the given name was found, let's create
		// one.
		columns := []string{"name", "driver"}
		values := []interface{}{name, driver}
		poolID, err = query.UpsertObject(c.tx, "storage_pools", columns, values)
		if err != nil {
			return err
		}
	} else {
		// Check that the existing pools matches the given driver and
		// is in the pending state.
		if pool.driver != driver {
			return fmt.Errorf("pool already exists with a different driver")
		}
		if pool.state != storagePoolPending {
			return fmt.Errorf("pool 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 storage_pool entry of this node and pool exists yet.
	count, err := query.Count(
		c.tx, "storage_pools_nodes", "storage_pool_id=? AND node_id=?", poolID, nodeInfo.ID)
	if err != nil {
		return err
	}
	if count != 0 {
		return ErrAlreadyDefined
	}

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

	return nil
}

// StoragePoolCreatePending creates a new pending storage pool on the node with
// the given name.

StoragePoolCreatePending creates a new pending storage pool on the node with the given name .


func (c *ClusterTx) StoragePoolCreated(name string) error {
	return c.storagePoolState(name, storagePoolCreated)
}

func ( c * ClusterTx ) StoragePoolCreated ( name string ) error { return c . storagePoolState ( name , storagePoolCreated ) }

// StoragePoolCreated sets the state of the given pool to "Created".

StoragePoolCreated sets the state of the given pool to Created .


func (c *ClusterTx) StoragePoolErrored(name string) error {
	return c.storagePoolState(name, storagePoolErrored)
}

func ( c * ClusterTx ) StoragePoolErrored ( name string ) error { return c . storagePoolState ( name , storagePoolErrored ) }

// StoragePoolErrored sets the state of the given pool to "Errored".

StoragePoolErrored sets the state of the given pool to Errored .


func (c *ClusterTx) StoragePoolNodeConfigs(poolID int64) (map[string]map[string]string, error) {
	// Fetch all nodes.
	nodes, err := c.Nodes()
	if err != nil {
		return nil, err
	}

	// Fetch the names of the nodes where the storage pool is defined.
	stmt := `
SELECT nodes.name FROM nodes
  LEFT JOIN storage_pools_nodes ON storage_pools_nodes.node_id = nodes.id
  LEFT JOIN storage_pools ON storage_pools_nodes.storage_pool_id = storage_pools.id
WHERE storage_pools.id = ? AND storage_pools.state = ?
`
	defined, err := query.SelectStrings(c.tx, stmt, poolID, storagePoolPending)
	if err != nil {
		return nil, err
	}

	// Figure which nodes are missing
	missing := []string{}
	for _, node := range nodes {
		if !shared.StringInSlice(node.Name, defined) {
			missing = append(missing, node.Name)
		}
	}

	if len(missing) > 0 {
		return nil, fmt.Errorf("Pool not defined on nodes: %s", strings.Join(missing, ", "))
	}

	configs := map[string]map[string]string{}
	for _, node := range nodes {
		config, err := query.SelectConfig(
			c.tx, "storage_pools_config", "storage_pool_id=? AND node_id=?", poolID, node.ID)
		if err != nil {
			return nil, err
		}
		configs[node.Name] = config
	}

	return configs, nil
}

// StoragePoolNodeConfigs returns the node-specific configuration of all
// nodes grouped by node name, for the given poolID.
//
// If the storage pool is not defined on all nodes, an error is returned.

StoragePoolNodeConfigs returns the node - specific configuration of all nodes grouped by node name for the given poolID . If the storage pool is not defined on all nodes an error is returned .


func (c *Cluster) StoragePoolsGetDrivers() ([]string, error) {
	var poolDriver string
	query := "SELECT DISTINCT driver FROM storage_pools"
	inargs := []interface{}{}
	outargs := []interface{}{poolDriver}

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

	if len(result) == 0 {
		return []string{}, ErrNoSuchObject
	}

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

	return drivers, nil
}

func ( c * Cluster ) StoragePoolsGetDrivers ( ) ( [ ] string , error ) { var poolDriver string query := " " inargs := [ ] interface { } { } outargs := [ ] interface { } { poolDriver } result , err := queryScan ( c . db , query , inargs , outargs ) if err != nil { return [ ] string { } , err } if len ( result ) == 0 { return [ ] string { } , ErrNoSuchObject } drivers := [ ] string { } for _ , driver := range result { drivers = append ( drivers , driver [ 0 ] . ( string ) ) } return drivers , nil }

// StoragePoolsGetDrivers returns the names of all storage volumes attached to
// a given storage pool.

StoragePoolsGetDrivers returns the names of all storage volumes attached to a given storage pool .


func (c *Cluster) StoragePoolGetID(poolName string) (int64, error) {
	poolID := int64(-1)
	query := "SELECT id FROM storage_pools WHERE name=?"
	inargs := []interface{}{poolName}
	outargs := []interface{}{&poolID}

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

	return poolID, nil
}

func ( c * Cluster ) StoragePoolGetID ( poolName string ) ( int64 , error ) { poolID := int64 ( - 1 ) query := " " inargs := [ ] interface { } { poolName } outargs := [ ] interface { } { & poolID } err := dbQueryRowScan ( c . db , query , inargs , outargs ) if err != nil { if err == sql . ErrNoRows { return - 1 , ErrNoSuchObject } } return poolID , nil }

// StoragePoolGetID returns the id of a single storage pool.

StoragePoolGetID returns the id of a single storage pool .


func (c *Cluster) StoragePoolGet(poolName string) (int64, *api.StoragePool, error) {
	var poolDriver string
	poolID := int64(-1)
	description := sql.NullString{}
	var state int

	query := "SELECT id, driver, description, state FROM storage_pools WHERE name=?"
	inargs := []interface{}{poolName}
	outargs := []interface{}{&poolID, &poolDriver, &description, &state}

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

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

	storagePool := api.StoragePool{
		Name:   poolName,
		Driver: poolDriver,
	}
	storagePool.Description = description.String
	storagePool.Config = config

	switch state {
	case storagePoolPending:
		storagePool.Status = "Pending"
	case storagePoolCreated:
		storagePool.Status = "Created"
	default:
		storagePool.Status = "Unknown"
	}

	nodes, err := c.storagePoolNodes(poolID)
	if err != nil {
		return -1, nil, err
	}
	storagePool.Locations = nodes

	return poolID, &storagePool, nil
}

func ( c * Cluster ) StoragePoolGet ( poolName string ) ( int64 , * api . StoragePool , error ) { var poolDriver string poolID := int64 ( - 1 ) description := sql . NullString { } var state int query := " " inargs := [ ] interface { } { poolName } outargs := [ ] interface { } { & poolID , & poolDriver , & description , & state } err := dbQueryRowScan ( c . db , query , inargs , outargs ) if err != nil { if err == sql . ErrNoRows { return - 1 , nil , ErrNoSuchObject } return - 1 , nil , err } config , err := c . StoragePoolConfigGet ( poolID ) if err != nil { return - 1 , nil , err } storagePool := api . StoragePool { Name : poolName , Driver : poolDriver , } storagePool . Description = description . String storagePool . Config = config switch state { case storagePoolPending : storagePool . Status = " " case storagePoolCreated : storagePool . Status = " " default : storagePool . Status = " " } nodes , err := c . storagePoolNodes ( poolID ) if err != nil { return - 1 , nil , err } storagePool . Locations = nodes return poolID , & storagePool , nil }

// StoragePoolGet returns a single storage pool.

StoragePoolGet returns a single storage pool .


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

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

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


func (c *Cluster) StoragePoolConfigGet(poolID int64) (map[string]string, error) {
	var key, value string
	query := "SELECT key, value FROM storage_pools_config WHERE storage_pool_id=? AND (node_id=? OR node_id IS NULL)"
	inargs := []interface{}{poolID, c.nodeID}
	outargs := []interface{}{key, value}

	results, err := queryScan(c.db, query, inargs, outargs)
	if err != nil {
		return nil, err
	}

	config := map[string]string{}

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

		config[key] = value
	}

	return config, nil
}

func ( c * Cluster ) StoragePoolConfigGet ( poolID int64 ) ( map [ string ] string , error ) { var key , value string query := " " inargs := [ ] interface { } { poolID , c . nodeID } outargs := [ ] interface { } { key , value } results , err := queryScan ( c . db , query , inargs , outargs ) if err != nil { return nil , err } config := map [ string ] string { } for _ , r := range results { key = r [ 0 ] . ( string ) value = r [ 1 ] . ( string ) config [ key ] = value } return config , nil }

// StoragePoolConfigGet returns the config of a storage pool.

StoragePoolConfigGet returns the config of a storage pool .


func (c *Cluster) StoragePoolCreate(poolName string, poolDescription string, poolDriver string, poolConfig map[string]string) (int64, error) {
	var id int64
	err := c.Transaction(func(tx *ClusterTx) error {
		result, err := tx.tx.Exec("INSERT INTO storage_pools (name, description, driver, state) VALUES (?, ?, ?, ?)", poolName, poolDescription, poolDriver, storagePoolCreated)
		if err != nil {
			return err
		}

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

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

		err = storagePoolConfigAdd(tx.tx, id, c.nodeID, poolConfig)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		id = -1
	}

	return id, nil
}

// StoragePoolCreate creates new storage pool.

StoragePoolCreate creates new storage pool .


func storagePoolConfigAdd(tx *sql.Tx, poolID, nodeID int64, poolConfig map[string]string) error {
	str := "INSERT INTO storage_pools_config (storage_pool_id, node_id, key, value) VALUES(?, ?, ?, ?)"
	stmt, err := tx.Prepare(str)
	defer stmt.Close()
	if err != nil {
		return err
	}

	for k, v := range poolConfig {
		if v == "" {
			continue
		}
		var nodeIDValue interface{}
		if !shared.StringInSlice(k, StoragePoolNodeConfigKeys) {
			nodeIDValue = nil
		} else {
			nodeIDValue = nodeID
		}

		_, err = stmt.Exec(poolID, nodeIDValue, k, v)
		if err != nil {
			return err
		}
	}

	return nil
}

func storagePoolConfigAdd ( tx * sql . Tx , poolID , nodeID int64 , poolConfig map [ string ] string ) error { str := " " stmt , err := tx . Prepare ( str ) defer stmt . Close ( ) if err != nil { return err } for k , v := range poolConfig { if v == " " { continue } var nodeIDValue interface { } if ! shared . StringInSlice ( k , StoragePoolNodeConfigKeys ) { nodeIDValue = nil } else { nodeIDValue = nodeID } _ , err = stmt . Exec ( poolID , nodeIDValue , k , v ) if err != nil { return err } } return nil }


func (c *Cluster) StoragePoolUpdate(poolName, description string, poolConfig map[string]string) error {
	poolID, _, err := c.StoragePoolGet(poolName)
	if err != nil {
		return err
	}

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

		err = StoragePoolConfigClear(tx.tx, poolID, c.nodeID)
		if err != nil {
			return err
		}

		err = storagePoolConfigAdd(tx.tx, poolID, c.nodeID, poolConfig)
		if err != nil {
			return err
		}
		return nil
	})

	return err
}

func ( c * Cluster ) StoragePoolUpdate ( poolName , description string , poolConfig map [ string ] string ) error { poolID , _ , err := c . StoragePoolGet ( poolName ) if err != nil { return err } err = c . Transaction ( func ( tx * ClusterTx ) error { err = StoragePoolUpdateDescription ( tx . tx , poolID , description ) if err != nil { return err } err = StoragePoolConfigClear ( tx . tx , poolID , c . nodeID ) if err != nil { return err } err = storagePoolConfigAdd ( tx . tx , poolID , c . nodeID , poolConfig ) if err != nil { return err } return nil } ) return err }

// StoragePoolUpdate updates a storage pool.

StoragePoolUpdate updates a storage pool .


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

	return nil
}

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

// StoragePoolConfigClear deletes the storage pool config.

StoragePoolConfigClear deletes the storage pool config .


func (c *Cluster) StoragePoolDelete(poolName string) (*api.StoragePool, error) {
	poolID, pool, err := c.StoragePoolGet(poolName)
	if err != nil {
		return nil, err
	}

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

	return pool, nil
}

func ( c * Cluster ) StoragePoolDelete ( poolName string ) ( * api . StoragePool , error ) { poolID , pool , err := c . StoragePoolGet ( poolName ) if err != nil { return nil , err } err = exec ( c . db , " " , poolID ) if err != nil { return nil , err } return pool , nil }

// StoragePoolDelete deletes storage pool.

StoragePoolDelete deletes storage pool .


func (c *Cluster) StoragePoolVolumesGetNames(poolID int64) ([]string, error) {
	var volumeName string
	query := "SELECT name FROM storage_volumes WHERE storage_pool_id=? AND node_id=?"
	inargs := []interface{}{poolID, c.nodeID}
	outargs := []interface{}{volumeName}

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

	var out []string

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

	return out, nil
}

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

// StoragePoolVolumesGetNames gets the names of all storage volumes attached to
// a given storage pool.

StoragePoolVolumesGetNames gets the names of all storage volumes attached to a given storage pool .


func (c *Cluster) StoragePoolVolumesGet(project string, poolID int64, volumeTypes []int) ([]*api.StorageVolume, error) {
	var nodeIDs []int

	err := c.Transaction(func(tx *ClusterTx) error {
		var err error
		nodeIDs, err = query.SelectIntegers(tx.tx, `
SELECT DISTINCT node_id
  FROM storage_volumes
  JOIN projects ON projects.id = storage_volumes.project_id
 WHERE (projects.name=? OR storage_volumes.type=?) AND storage_pool_id=?
`, project, StoragePoolVolumeTypeCustom, poolID)
		return err
	})
	if err != nil {
		return nil, err
	}
	volumes := []*api.StorageVolume{}

	for _, nodeID := range nodeIDs {
		nodeVolumes, err := c.storagePoolVolumesGet(project, poolID, int64(nodeID), volumeTypes)
		if err != nil {
			return nil, err
		}
		volumes = append(volumes, nodeVolumes...)
	}
	return volumes, nil
}

// StoragePoolVolumesGet returns all storage volumes attached to a given
// storage pool on any node.

StoragePoolVolumesGet returns all storage volumes attached to a given storage pool on any node .


func (c *Cluster) StoragePoolNodeVolumesGet(poolID int64, volumeTypes []int) ([]*api.StorageVolume, error) {
	return c.storagePoolVolumesGet("default", poolID, c.nodeID, volumeTypes)
}

func ( c * Cluster ) StoragePoolNodeVolumesGet ( poolID int64 , volumeTypes [ ] int ) ( [ ] * api . StorageVolume , error ) { return c . storagePoolVolumesGet ( " " , poolID , c . nodeID , volumeTypes ) }

// StoragePoolNodeVolumesGet returns all storage volumes attached to a given
// storage pool on the current node.

StoragePoolNodeVolumesGet returns all storage volumes attached to a given storage pool on the current node .


func (c *Cluster) storagePoolVolumesGet(project string, poolID, nodeID int64, volumeTypes []int) ([]*api.StorageVolume, error) {
	// Get all storage volumes of all types attached to a given storage
	// pool.
	result := []*api.StorageVolume{}
	for _, volumeType := range volumeTypes {
		volumeNames, err := c.StoragePoolVolumesGetType(project, volumeType, poolID, nodeID)
		if err != nil && err != sql.ErrNoRows {
			return nil, errors.Wrap(err, "failed to fetch volume types")
		}
		for _, volumeName := range volumeNames {
			_, volume, err := c.StoragePoolVolumeGetType(project, volumeName, volumeType, poolID, nodeID)
			if err != nil {
				return nil, errors.Wrap(err, "failed to fetch volume type")
			}
			result = append(result, volume)
		}
	}

	if len(result) == 0 {
		return result, ErrNoSuchObject
	}

	return result, nil
}

// Returns all storage volumes attached to a given storage pool on the given
// node.

Returns all storage volumes attached to a given storage pool on the given node .


func (c *Cluster) StoragePoolVolumesGetType(project string, volumeType int, poolID, nodeID int64) ([]string, error) {
	var poolName string
	query := `
SELECT storage_volumes.name
  FROM storage_volumes
  JOIN projects ON projects.id=storage_volumes.project_id
 WHERE (projects.name=? OR storage_volumes.type=?) AND storage_pool_id=? AND node_id=? AND type=?
`
	inargs := []interface{}{project, StoragePoolVolumeTypeCustom, poolID, nodeID, volumeType}
	outargs := []interface{}{poolName}

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

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

	return response, nil
}

// StoragePoolVolumesGetType get all storage volumes attached to a given
// storage pool of a given volume type, on the given node.

StoragePoolVolumesGetType get all storage volumes attached to a given storage pool of a given volume type on the given node .


func (c *Cluster) StoragePoolVolumeSnapshotsGetType(volumeName string, volumeType int, poolID int64) ([]string, error) {
	result := []string{}
	regexp := volumeName + shared.SnapshotDelimiter
	length := len(regexp)

	query := "SELECT name FROM storage_volumes WHERE storage_pool_id=? AND node_id=? AND type=? AND snapshot=? AND SUBSTR(name,1,?)=?"
	inargs := []interface{}{poolID, c.nodeID, volumeType, true, length, regexp}
	outfmt := []interface{}{volumeName}

	dbResults, err := queryScan(c.db, query, inargs, outfmt)
	if err != nil {
		return result, err
	}

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

	return result, nil
}

func ( c * Cluster ) StoragePoolVolumeSnapshotsGetType ( volumeName string , volumeType int , poolID int64 ) ( [ ] string , error ) { result := [ ] string { } regexp := volumeName + shared . SnapshotDelimiter length := len ( regexp ) query := " " inargs := [ ] interface { } { poolID , c . nodeID , volumeType , true , length , regexp } outfmt := [ ] interface { } { volumeName } dbResults , err := queryScan ( c . db , query , inargs , outfmt ) if err != nil { return result , err } for _ , r := range dbResults { result = append ( result , r [ 0 ] . ( string ) ) } return result , nil }

// StoragePoolVolumeSnapshotsGetType get all snapshots of a storage volume
// attached to a given storage pool of a given volume type, on the given node.

StoragePoolVolumeSnapshotsGetType get all snapshots of a storage volume attached to a given storage pool of a given volume type on the given node .


func (c *Cluster) StoragePoolNodeVolumesGetType(volumeType int, poolID int64) ([]string, error) {
	return c.StoragePoolVolumesGetType("default", volumeType, poolID, c.nodeID)
}

func ( c * Cluster ) StoragePoolNodeVolumesGetType ( volumeType int , poolID int64 ) ( [ ] string , error ) { return c . StoragePoolVolumesGetType ( " " , volumeType , poolID , c . nodeID ) }

// StoragePoolNodeVolumesGetType returns all storage volumes attached to a
// given storage pool of a given volume type, on the current node.

StoragePoolNodeVolumesGetType returns all storage volumes attached to a given storage pool of a given volume type on the current node .


func (c *Cluster) StoragePoolVolumeGetType(project string, volumeName string, volumeType int, poolID, nodeID int64) (int64, *api.StorageVolume, error) {
	// Custom volumes are "global", i.e. they are associated with the
	// default project.
	if volumeType == StoragePoolVolumeTypeCustom {
		project = "default"
	}

	volumeID, err := c.StoragePoolVolumeGetTypeID(project, volumeName, volumeType, poolID, nodeID)
	if err != nil {
		return -1, nil, err
	}

	volumeNode, err := c.StorageVolumeNodeGet(volumeID)
	if err != nil {
		return -1, nil, err
	}

	volumeConfig, err := c.StorageVolumeConfigGet(volumeID)
	if err != nil {
		return -1, nil, err
	}

	volumeDescription, err := c.StorageVolumeDescriptionGet(volumeID)
	if err != nil {
		return -1, nil, err
	}

	volumeTypeName, err := StoragePoolVolumeTypeToName(volumeType)
	if err != nil {
		return -1, nil, err
	}

	storageVolume := api.StorageVolume{
		Type: volumeTypeName,
	}
	storageVolume.Name = volumeName
	storageVolume.Description = volumeDescription
	storageVolume.Config = volumeConfig
	storageVolume.Location = volumeNode

	return volumeID, &storageVolume, nil
}

// StoragePoolVolumeGetType returns a single storage volume attached to a
// given storage pool of a given type, on the node with the given ID.

StoragePoolVolumeGetType returns a single storage volume attached to a given storage pool of a given type on the node with the given ID .


func (c *Cluster) StoragePoolNodeVolumeGetType(volumeName string, volumeType int, poolID int64) (int64, *api.StorageVolume, error) {
	return c.StoragePoolNodeVolumeGetTypeByProject("default", volumeName, volumeType, poolID)
}

func ( c * Cluster ) StoragePoolNodeVolumeGetType ( volumeName string , volumeType int , poolID int64 ) ( int64 , * api . StorageVolume , error ) { return c . StoragePoolNodeVolumeGetTypeByProject ( " " , volumeName , volumeType , poolID ) }

// StoragePoolNodeVolumeGetType gets a single storage volume attached to a
// given storage pool of a given type, on the current node.

StoragePoolNodeVolumeGetType gets a single storage volume attached to a given storage pool of a given type on the current node .


func (c *Cluster) StoragePoolNodeVolumeGetTypeByProject(project, volumeName string, volumeType int, poolID int64) (int64, *api.StorageVolume, error) {
	return c.StoragePoolVolumeGetType(project, volumeName, volumeType, poolID, c.nodeID)
}

func ( c * Cluster ) StoragePoolNodeVolumeGetTypeByProject ( project , volumeName string , volumeType int , poolID int64 ) ( int64 , * api . StorageVolume , error ) { return c . StoragePoolVolumeGetType ( project , volumeName , volumeType , poolID , c . nodeID ) }

// StoragePoolNodeVolumeGetTypeByProject gets a single storage volume attached to a
// given storage pool of a given type, on the current node in the given project.

StoragePoolNodeVolumeGetTypeByProject gets a single storage volume attached to a given storage pool of a given type on the current node in the given project .


func (c *Cluster) StoragePoolVolumeUpdate(volumeName string, volumeType int, poolID int64, volumeDescription string, volumeConfig map[string]string) error {
	volumeID, _, err := c.StoragePoolNodeVolumeGetType(volumeName, volumeType, poolID)
	if err != nil {
		return err
	}

	err = c.Transaction(func(tx *ClusterTx) error {
		err = storagePoolVolumeReplicateIfCeph(tx.tx, volumeID, "default", volumeName, volumeType, poolID, func(volumeID int64) error {
			err = StorageVolumeConfigClear(tx.tx, volumeID)
			if err != nil {
				return err
			}

			err = StorageVolumeConfigAdd(tx.tx, volumeID, volumeConfig)
			if err != nil {
				return err
			}

			return StorageVolumeDescriptionUpdate(tx.tx, volumeID, volumeDescription)
		})
		if err != nil {
			return err
		}
		return nil
	})

	return err
}

func ( c * Cluster ) StoragePoolVolumeUpdate ( volumeName string , volumeType int , poolID int64 , volumeDescription string , volumeConfig map [ string ] string ) error { volumeID , _ , err := c . StoragePoolNodeVolumeGetType ( volumeName , volumeType , poolID ) if err != nil { return err } err = c . Transaction ( func ( tx * ClusterTx ) error { err = storagePoolVolumeReplicateIfCeph ( tx . tx , volumeID , " " , volumeName , volumeType , poolID , func ( volumeID int64 ) error { err = StorageVolumeConfigClear ( tx . tx , volumeID ) if err != nil { return err } err = StorageVolumeConfigAdd ( tx . tx , volumeID , volumeConfig ) if err != nil { return err } return StorageVolumeDescriptionUpdate ( tx . tx , volumeID , volumeDescription ) } ) if err != nil { return err } return nil } ) return err }

// StoragePoolVolumeUpdate updates the storage volume attached to a given storage
// pool.

StoragePoolVolumeUpdate updates the storage volume attached to a given storage pool .


func (c *Cluster) StoragePoolVolumeDelete(project, volumeName string, volumeType int, poolID int64) error {
	volumeID, _, err := c.StoragePoolNodeVolumeGetTypeByProject(project, volumeName, volumeType, poolID)
	if err != nil {
		return err
	}

	err = c.Transaction(func(tx *ClusterTx) error {
		err := storagePoolVolumeReplicateIfCeph(tx.tx, volumeID, project, volumeName, volumeType, poolID, func(volumeID int64) error {
			_, err := tx.tx.Exec("DELETE FROM storage_volumes WHERE id=?", volumeID)
			return err
		})
		return err
	})

	return err
}

func ( c * Cluster ) StoragePoolVolumeDelete ( project , volumeName string , volumeType int , poolID int64 ) error { volumeID , _ , err := c . StoragePoolNodeVolumeGetTypeByProject ( project , volumeName , volumeType , poolID ) if err != nil { return err } err = c . Transaction ( func ( tx * ClusterTx ) error { err := storagePoolVolumeReplicateIfCeph ( tx . tx , volumeID , project , volumeName , volumeType , poolID , func ( volumeID int64 ) error { _ , err := tx . tx . Exec ( " " , volumeID ) return err } ) return err } ) return err }

// StoragePoolVolumeDelete deletes the storage volume attached to a given storage
// pool.

StoragePoolVolumeDelete deletes the storage volume attached to a given storage pool .


func (c *Cluster) StoragePoolVolumeRename(project, oldVolumeName string, newVolumeName string, volumeType int, poolID int64) error {
	volumeID, _, err := c.StoragePoolNodeVolumeGetTypeByProject(project, oldVolumeName, volumeType, poolID)
	if err != nil {
		return err
	}

	err = c.Transaction(func(tx *ClusterTx) error {
		err := storagePoolVolumeReplicateIfCeph(tx.tx, volumeID, project, oldVolumeName, volumeType, poolID, func(volumeID int64) error {
			_, err := tx.tx.Exec("UPDATE storage_volumes SET name=? WHERE id=? AND type=?", newVolumeName, volumeID, volumeType)
			return err
		})
		return err
	})

	return err
}

func ( c * Cluster ) StoragePoolVolumeRename ( project , oldVolumeName string , newVolumeName string , volumeType int , poolID int64 ) error { volumeID , _ , err := c . StoragePoolNodeVolumeGetTypeByProject ( project , oldVolumeName , volumeType , poolID ) if err != nil { return err } err = c . Transaction ( func ( tx * ClusterTx ) error { err := storagePoolVolumeReplicateIfCeph ( tx . tx , volumeID , project , oldVolumeName , volumeType , poolID , func ( volumeID int64 ) error { _ , err := tx . tx . Exec ( " " , newVolumeName , volumeID , volumeType ) return err } ) return err } ) return err }

// StoragePoolVolumeRename renames the storage volume attached to a given storage pool.

StoragePoolVolumeRename renames the storage volume attached to a given storage pool .


func storagePoolVolumeReplicateIfCeph(tx *sql.Tx, volumeID int64, project, volumeName string, volumeType int, poolID int64, f func(int64) error) error {
	driver, err := storagePoolDriverGet(tx, poolID)
	if err != nil {
		return err
	}
	volumeIDs := []int64{volumeID}

	// If this is a ceph volume, we want to duplicate the change across the
	// the rows for all other nodes.
	if driver == "ceph" {
		volumeIDs, err = storageVolumeIDsGet(tx, project, volumeName, volumeType, poolID)
		if err != nil {
			return err
		}
	}

	for _, volumeID := range volumeIDs {
		err := f(volumeID)
		if err != nil {
			return err
		}
	}

	return nil
}

// This a convenience to replicate a certain volume change to all nodes if the
// underlying driver is ceph.

This a convenience to replicate a certain volume change to all nodes if the underlying driver is ceph .