func (c *Cluster) StoragePoolVolumeCreate(project, volumeName, volumeDescription string, volumeType int, snapshot bool, poolID int64, volumeConfig map[string]string) (int64, error) {
	var thisVolumeID int64

	err := c.Transaction(func(tx *ClusterTx) error {
		nodeIDs := []int{int(c.nodeID)}
		driver, err := storagePoolDriverGet(tx.tx, poolID)
		if err != nil {
			return err
		}
		// If the driver is ceph, create a volume entry for each node.
		if driver == "ceph" {
			nodeIDs, err = query.SelectIntegers(tx.tx, "SELECT id FROM nodes")
			if err != nil {
				return err
			}
		}

		for _, nodeID := range nodeIDs {
			result, err := tx.tx.Exec(`
INSERT INTO storage_volumes (storage_pool_id, node_id, type, snapshot, name, description, project_id) VALUES (?, ?, ?, ?, ?, ?, (SELECT id FROM projects WHERE name = ?))
`,
				poolID, nodeID, volumeType, snapshot, volumeName, volumeDescription, project)
			if err != nil {
				return err
			}

			volumeID, err := result.LastInsertId()
			if err != nil {
				return err
			}
			if int64(nodeID) == c.nodeID {
				// Return the ID of the volume created on this node.
				thisVolumeID = volumeID
			}

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

	return thisVolumeID, err
}

// StoragePoolVolumeCreate creates a new storage volume attached to a given
// storage pool.

StoragePoolVolumeCreate creates a new storage volume attached to a given storage pool .


func (c *Cluster) StoragePoolVolumeGetTypeID(project string, volumeName string, volumeType int, poolID, nodeID int64) (int64, error) {
	volumeID := int64(-1)
	query := `SELECT storage_volumes.id
FROM storage_volumes
JOIN storage_pools ON storage_volumes.storage_pool_id = storage_pools.id
JOIN projects ON storage_volumes.project_id = projects.id
WHERE projects.name=? AND storage_volumes.storage_pool_id=? AND storage_volumes.node_id=?
AND storage_volumes.name=? AND storage_volumes.type=?`
	inargs := []interface{}{project, poolID, nodeID, volumeName, volumeType}
	outargs := []interface{}{&volumeID}

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

	return volumeID, nil
}

// StoragePoolVolumeGetTypeID returns the ID of a storage volume on a given
// storage pool of a given storage volume type, on the given node.

StoragePoolVolumeGetTypeID returns the ID of a storage volume on a given storage pool of a given storage volume type on the given node .


func (c *Cluster) StoragePoolNodeVolumeGetTypeID(volumeName string, volumeType int, poolID int64) (int64, error) {
	return c.StoragePoolVolumeGetTypeID("default", volumeName, volumeType, poolID, c.nodeID)
}

func ( c * Cluster ) StoragePoolNodeVolumeGetTypeID ( volumeName string , volumeType int , poolID int64 ) ( int64 , error ) { return c . StoragePoolVolumeGetTypeID ( " " , volumeName , volumeType , poolID , c . nodeID ) }

// StoragePoolNodeVolumeGetTypeID get the ID of a storage volume on a given
// storage pool of a given storage volume type, on the current node.

StoragePoolNodeVolumeGetTypeID get the ID of a storage volume on a given storage pool of a given storage volume type on the current node .


func StoragePoolVolumeTypeToName(volumeType int) (string, error) {
	switch volumeType {
	case StoragePoolVolumeTypeContainer:
		return StoragePoolVolumeTypeNameContainer, nil
	case StoragePoolVolumeTypeImage:
		return StoragePoolVolumeTypeNameImage, nil
	case StoragePoolVolumeTypeCustom:
		return StoragePoolVolumeTypeNameCustom, nil
	}

	return "", fmt.Errorf("invalid storage volume type")
}

func StoragePoolVolumeTypeToName ( volumeType int ) ( string , error ) { switch volumeType { case StoragePoolVolumeTypeContainer : return StoragePoolVolumeTypeNameContainer , nil case StoragePoolVolumeTypeImage : return StoragePoolVolumeTypeNameImage , nil case StoragePoolVolumeTypeCustom : return StoragePoolVolumeTypeNameCustom , nil } return " " , fmt . Errorf ( " " ) }

// StoragePoolVolumeTypeToName converts a volume integer type code to its
// human-readable name.

StoragePoolVolumeTypeToName converts a volume integer type code to its human - readable name .


func DevicesAdd(tx *sql.Tx, w string, cID int64, devices types.Devices) error {
	// Prepare the devices entry SQL
	str1 := fmt.Sprintf("INSERT INTO %ss_devices (%s_id, name, type) VALUES (?, ?, ?)", w, w)
	stmt1, err := tx.Prepare(str1)
	if err != nil {
		return err
	}
	defer stmt1.Close()

	// Prepare the devices config entry SQL
	str2 := fmt.Sprintf("INSERT INTO %ss_devices_config (%s_device_id, key, value) VALUES (?, ?, ?)", w, w)
	stmt2, err := tx.Prepare(str2)
	if err != nil {
		return err
	}
	defer stmt2.Close()

	// Insert all the devices
	for k, v := range devices {
		t, err := dbDeviceTypeToInt(v["type"])
		if err != nil {
			return err
		}

		result, err := stmt1.Exec(cID, k, t)
		if err != nil {
			return err
		}

		id64, err := result.LastInsertId()
		if err != nil {
			return fmt.Errorf("Error inserting device %s into database", k)
		}
		id := int(id64)

		for ck, cv := range v {
			// The type is stored as int in the parent entry
			if ck == "type" || cv == "" {
				continue
			}

			_, err = stmt2.Exec(id, ck, cv)
			if err != nil {
				return err
			}
		}
	}

	return nil
}


func (c *Cluster) Devices(project, qName string, isprofile bool) (types.Devices, error) {
	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasProfiles(project)
		if err != nil {
			return err
		}
		if !enabled {
			project = "default"
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	var q string
	if isprofile {
		q = `SELECT profiles_devices.id, profiles_devices.name, profiles_devices.type
			FROM profiles_devices
                        JOIN profiles ON profiles_devices.profile_id = profiles.id
                        JOIN projects ON projects.id=profiles.project_id
   		WHERE projects.name=? AND profiles.name=?`
	} else {
		q = `SELECT containers_devices.id, containers_devices.name, containers_devices.type
			FROM containers_devices
                        JOIN containers	ON containers_devices.container_id = containers.id
                        JOIN projects ON projects.id=containers.project_id
			WHERE projects.name=? AND containers.name=?`
	}
	var id, dtype int
	var name, stype string
	inargs := []interface{}{project, qName}
	outfmt := []interface{}{id, name, dtype}
	results, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return nil, err
	}

	devices := types.Devices{}
	for _, r := range results {
		id = r[0].(int)
		name = r[1].(string)
		stype, err = dbDeviceTypeToString(r[2].(int))
		if err != nil {
			return nil, err
		}
		newdev, err := dbDeviceConfig(c.db, id, isprofile)
		if err != nil {
			return nil, err
		}
		newdev["type"] = stype
		devices[name] = newdev
	}

	return devices, nil
}

// Devices returns the devices matching the given filters.

Devices returns the devices matching the given filters .


func (n *Node) Patches() ([]string, error) {
	inargs := []interface{}{}
	outfmt := []interface{}{""}

	query := fmt.Sprintf("SELECT name FROM patches")
	result, err := queryScan(n.db, query, inargs, outfmt)
	if err != nil {
		return []string{}, err
	}

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

	return response, nil
}

func ( n * Node ) Patches ( ) ( [ ] string , error ) { inargs := [ ] interface { } { } outfmt := [ ] interface { } { " " } query := fmt . Sprintf ( " " ) result , err := queryScan ( n . db , query , inargs , outfmt ) if err != nil { return [ ] string { } , err } response := [ ] string { } for _ , r := range result { response = append ( response , r [ 0 ] . ( string ) ) } return response , nil }

// Patches returns the names of all patches currently applied on this node.

Patches returns the names of all patches currently applied on this node .


func (n *Node) PatchesMarkApplied(patch string) error {
	stmt := `INSERT INTO patches (name, applied_at) VALUES (?, strftime("%s"));`
	_, err := n.db.Exec(stmt, patch)
	return err
}

// PatchesMarkApplied marks the patch with the given name as applied on this node.

PatchesMarkApplied marks the patch with the given name as applied on this node .


func entityType(pkg string, entity string) string {
	typ := lex.Capital(entity)
	if pkg != "db" {
		typ = pkg + "." + typ
	}
	return typ
}

func entityType ( pkg string , entity string ) string { typ := lex . Capital ( entity ) if pkg != " " { typ = pkg + " " + typ } return typ }

// Return Go type of the given database entity.

Return Go type of the given database entity .


func entityPost(entity string) string {
	return fmt.Sprintf("%sPost", lex.Capital(lex.Plural(entity)))
}

func entityPost ( entity string ) string { return fmt . Sprintf ( " " , lex . Capital ( lex . Plural ( entity ) ) ) }

// Return the name of the Post struct for the given entity.

Return the name of the Post struct for the given entity .


func stmtCodeVar(entity string, kind string, filters ...string) string {
	name := fmt.Sprintf("%s%s", entity, lex.Camel(kind))

	if len(filters) > 0 {
		name += "By"
		name += strings.Join(filters, "And")
	}

	return name
}

func stmtCodeVar ( entity string , kind string , filters ... string ) string { name := fmt . Sprintf ( " " , entity , lex . Camel ( kind ) ) if len ( filters ) > 0 { name += " " name += strings . Join ( filters , " " ) } return name }

// Return the name of the global variable holding the registration code for
// the given kind of statement aganst the given entity.

Return the name of the global variable holding the registration code for the given kind of statement aganst the given entity .


func destFunc(slice string, typ string, fields []*Field) string {
	f := fmt.Sprintf(`func(i int) []interface{} {
                      %s = append(%s, %s{})
                      return []interface{}{
`, slice, slice, typ)

	for _, field := range fields {
		f += fmt.Sprintf("&%s[i].%s,\n", slice, field.Name)
	}

	f += "        }\n"
	f += "}"

	return f
}

// Return the code for a "dest" function, to be passed as parameter to
// query.SelectObjects in order to scan a single row.

Return the code for a dest function to be passed as parameter to query . SelectObjects in order to scan a single row .


func CompareConfigs(config1, config2 map[string]string, exclude []string) error {
	if exclude == nil {
		exclude = []string{}
	}

	delta := []string{}
	for key, value := range config1 {
		if shared.StringInSlice(key, exclude) {
			continue
		}
		if config2[key] != value {
			delta = append(delta, key)
		}
	}
	for key, value := range config2 {
		if shared.StringInSlice(key, exclude) {
			continue
		}
		if config1[key] != value {
			present := false
			for i := range delta {
				if delta[i] == key {
					present = true
				}
				break
			}
			if !present {
				delta = append(delta, key)
			}
		}
	}
	sort.Strings(delta)
	if len(delta) > 0 {
		return fmt.Errorf("different values for keys: %s", strings.Join(delta, ", "))
	}

	return nil
}

func CompareConfigs ( config1 , config2 map [ string ] string , exclude [ ] string ) error { if exclude == nil { exclude = [ ] string { } } delta := [ ] string { } for key , value := range config1 { if shared . StringInSlice ( key , exclude ) { continue } if config2 [ key ] != value { delta = append ( delta , key ) } } for key , value := range config2 { if shared . StringInSlice ( key , exclude ) { continue } if config1 [ key ] != value { present := false for i := range delta { if delta [ i ] == key { present = true } break } if ! present { delta = append ( delta , key ) } } } sort . Strings ( delta ) if len ( delta ) > 0 { return fmt . Errorf ( " " , strings . Join ( delta , " " ) ) } return nil }

// CompareConfigs compares two config maps and returns an error if they differ.

CompareConfigs compares two config maps and returns an error if they differ .


func CopyConfig(config map[string]string) map[string]string {
	copy := map[string]string{}
	for key, value := range config {
		copy[key] = value
	}
	return copy
}

func CopyConfig ( config map [ string ] string ) map [ string ] string { copy := map [ string ] string { } for key , value := range config { copy [ key ] = value } return copy }

// CopyConfig creates a new map with a copy of the given config.

CopyConfig creates a new map with a copy of the given config .


func NewNotifier(state *state.State, cert *shared.CertInfo, policy NotifierPolicy) (Notifier, error) {
	address, err := node.ClusterAddress(state.Node)
	if err != nil {
		return nil, errors.Wrap(err, "failed to fetch node address")
	}

	// Fast-track the case where we're not clustered at all.
	if address == "" {
		nullNotifier := func(func(lxd.ContainerServer) error) error { return nil }
		return nullNotifier, nil
	}

	peers := []string{}
	err = state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		offlineThreshold, err := tx.NodeOfflineThreshold()
		if err != nil {
			return err
		}

		nodes, err := tx.Nodes()
		if err != nil {
			return err
		}
		for _, node := range nodes {
			if node.Address == address || node.Address == "0.0.0.0" {
				continue // Exclude ourselves
			}
			if node.IsOffline(offlineThreshold) {
				switch policy {
				case NotifyAll:
					return fmt.Errorf("peer node %s is down", node.Address)
				case NotifyAlive:
					continue // Just skip this node
				}
			}
			peers = append(peers, node.Address)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	notifier := func(hook func(lxd.ContainerServer) error) error {
		errs := make([]error, len(peers))
		wg := sync.WaitGroup{}
		wg.Add(len(peers))
		for i, address := range peers {
			logger.Debugf("Notify node %s of state changes", address)
			go func(i int, address string) {
				defer wg.Done()
				client, err := Connect(address, cert, true)
				if err != nil {
					errs[i] = errors.Wrapf(err, "failed to connect to peer %s", address)
					return
				}
				err = hook(client)
				if err != nil {
					errs[i] = errors.Wrapf(err, "failed to notify peer %s", address)
				}
			}(i, address)
		}
		wg.Wait()
		// TODO: aggregate all errors?
		for i, err := range errs {
			if err != nil {
				// FIXME: unfortunately the LXD client currently does not
				//        provide a way to differentiate between errors
				if isClientConnectionError(err) && policy == NotifyAlive {
					logger.Warnf("Could not notify node %s", peers[i])
					continue
				}
				return err
			}
		}
		return nil
	}

	return notifier, nil
}

// NewNotifier builds a Notifier that can be used to notify other peers using
// the given policy.

NewNotifier builds a Notifier that can be used to notify other peers using the given policy .


func Events(endpoints *endpoints.Endpoints, cluster *db.Cluster, f func(int64, api.Event)) (task.Func, task.Schedule) {
	listeners := map[int64]*lxd.EventListener{}

	// Update our pool of event listeners. Since database queries are
	// blocking, we spawn the actual logic in a goroutine, to abort
	// immediately when we receive the stop signal.
	update := func(ctx context.Context) {
		ch := make(chan struct{})
		go func() {
			eventsUpdateListeners(endpoints, cluster, listeners, f)
			ch <- struct{}{}
		}()
		select {
		case <-ch:
		case <-ctx.Done():
		}

	}

	schedule := task.Every(time.Second)

	return update, schedule
}

// Events starts a task that continuously monitors the list of cluster nodes and
// maintains a pool of websocket connections against all of them, in order to
// get notified about events.
//
// Whenever an event is received the given callback is invoked.

Events starts a task that continuously monitors the list of cluster nodes and maintains a pool of websocket connections against all of them in order to get notified about events . Whenever an event is received the given callback is invoked .


func eventsConnect(address string, cert *shared.CertInfo) (*lxd.EventListener, error) {
	client, err := Connect(address, cert, true)
	if err != nil {
		return nil, err
	}

	// Set the project to the special wildcard in order to get notified
	// about all events across all projects.
	client = client.UseProject("*")

	return client.GetEvents()
}

// Establish a client connection to get events from the given node.

Establish a client connection to get events from the given node .


func (s *storageDir) StoragePoolInit() error {
	err := s.StorageCoreInit()
	if err != nil {
		return err
	}

	return nil
}

func ( s * storageDir ) StoragePoolInit ( ) error { err := s . StorageCoreInit ( ) if err != nil { return err } return nil }


func getAAProfileContent(c container) string {
	profile := strings.TrimLeft(AA_PROFILE_BASE, "\n")

	// Apply new features
	if aaParserSupports("unix") {
		profile += `
  ### Feature: unix
  # Allow receive via unix sockets from anywhere
  unix (receive),

  # Allow all unix in the container
  unix peer=(label=@{profile_name}),
`
	}

	// Apply cgns bits
	if shared.PathExists("/proc/self/ns/cgroup") {
		profile += "\n  ### Feature: cgroup namespace\n"
		profile += "  mount fstype=cgroup -> /sys/fs/cgroup/**,\n"
		profile += "  mount fstype=cgroup2 -> /sys/fs/cgroup/**,\n"
	}

	state := c.DaemonState()
	if state.OS.AppArmorStacking && !state.OS.AppArmorStacked {
		profile += "\n  ### Feature: apparmor stacking\n"
		profile += `  ### Configuration: apparmor profile loading (in namespace)
  deny /sys/k[^e]*{,/**} wklx,
  deny /sys/ke[^r]*{,/**} wklx,
  deny /sys/ker[^n]*{,/**} wklx,
  deny /sys/kern[^e]*{,/**} wklx,
  deny /sys/kerne[^l]*{,/**} wklx,
  deny /sys/kernel/[^s]*{,/**} wklx,
  deny /sys/kernel/s[^e]*{,/**} wklx,
  deny /sys/kernel/se[^c]*{,/**} wklx,
  deny /sys/kernel/sec[^u]*{,/**} wklx,
  deny /sys/kernel/secu[^r]*{,/**} wklx,
  deny /sys/kernel/secur[^i]*{,/**} wklx,
  deny /sys/kernel/securi[^t]*{,/**} wklx,
  deny /sys/kernel/securit[^y]*{,/**} wklx,
  deny /sys/kernel/security/[^a]*{,/**} wklx,
  deny /sys/kernel/security/a[^p]*{,/**} wklx,
  deny /sys/kernel/security/ap[^p]*{,/**} wklx,
  deny /sys/kernel/security/app[^a]*{,/**} wklx,
  deny /sys/kernel/security/appa[^r]*{,/**} wklx,
  deny /sys/kernel/security/appar[^m]*{,/**} wklx,
  deny /sys/kernel/security/apparm[^o]*{,/**} wklx,
  deny /sys/kernel/security/apparmo[^r]*{,/**} wklx,
  deny /sys/kernel/security/apparmor?*{,/**} wklx,
  deny /sys/kernel/security?*{,/**} wklx,
  deny /sys/kernel?*{,/**} wklx,
`
		profile += fmt.Sprintf("  change_profile -> \":%s:*\",\n", AANamespace(c))
		profile += fmt.Sprintf("  change_profile -> \":%s://*\",\n", AANamespace(c))
	} else {
		profile += "\n  ### Feature: apparmor stacking (not present)\n"
		profile += "  deny /sys/k*{,/**} wklx,\n"
	}

	if c.IsNesting() {
		// Apply nesting bits
		profile += "\n  ### Configuration: nesting\n"
		profile += strings.TrimLeft(AA_PROFILE_NESTING, "\n")
		if !state.OS.AppArmorStacking || state.OS.AppArmorStacked {
			profile += fmt.Sprintf("  change_profile -> \"%s\",\n", AAProfileFull(c))
		}
	}

	if !c.IsPrivileged() || state.OS.RunningInUserNS {
		// Apply unprivileged bits
		profile += "\n  ### Configuration: unprivileged containers\n"
		profile += strings.TrimLeft(AA_PROFILE_UNPRIVILEGED, "\n")
	}

	// Append raw.apparmor
	rawApparmor, ok := c.ExpandedConfig()["raw.apparmor"]
	if ok {
		profile += "\n  ### Configuration: raw.apparmor\n"
		for _, line := range strings.Split(strings.Trim(rawApparmor, "\n"), "\n") {
			profile += fmt.Sprintf("  %s\n", line)
		}
	}

	return fmt.Sprintf(`#include <tunables/global>
profile "%s" flags=(attach_disconnected,mediate_deleted) {
%s
}
`, AAProfileFull(c), strings.Trim(profile, "\n"))
}

// getProfileContent generates the apparmor profile template from the given
// container. This includes the stock lxc includes as well as stuff from
// raw.apparmor.

getProfileContent generates the apparmor profile template from the given container . This includes the stock lxc includes as well as stuff from raw . apparmor .


func AALoadProfile(c container) error {
	state := c.DaemonState()
	if !state.OS.AppArmorAdmin {
		return nil
	}

	if err := mkApparmorNamespace(c, AANamespace(c)); err != nil {
		return err
	}

	/* In order to avoid forcing a profile parse (potentially slow) on
	 * every container start, let's use apparmor's binary policy cache,
	 * which checks mtime of the files to figure out if the policy needs to
	 * be regenerated.
	 *
	 * Since it uses mtimes, we shouldn't just always write out our local
	 * apparmor template; instead we should check to see whether the
	 * template is the same as ours. If it isn't we should write our
	 * version out so that the new changes are reflected and we definitely
	 * force a recompile.
	 */
	profile := path.Join(aaPath, "profiles", AAProfileShort(c))
	content, err := ioutil.ReadFile(profile)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	updated := getAAProfileContent(c)

	if string(content) != string(updated) {
		if err := os.MkdirAll(path.Join(aaPath, "cache"), 0700); err != nil {
			return err
		}

		if err := os.MkdirAll(path.Join(aaPath, "profiles"), 0700); err != nil {
			return err
		}

		if err := ioutil.WriteFile(profile, []byte(updated), 0600); err != nil {
			return err
		}
	}

	return runApparmor(APPARMOR_CMD_LOAD, c)
}

// Ensure that the container's policy is loaded into the kernel so the
// container can boot.

Ensure that the container s policy is loaded into the kernel so the container can boot .


func AADestroy(c container) error {
	state := c.DaemonState()
	if !state.OS.AppArmorAdmin {
		return nil
	}

	if state.OS.AppArmorStacking && !state.OS.AppArmorStacked {
		p := path.Join("/sys/kernel/security/apparmor/policy/namespaces", AANamespace(c))
		if err := os.Remove(p); err != nil {
			logger.Error("Error removing apparmor namespace", log.Ctx{"err": err, "ns": p})
		}
	}

	return runApparmor(APPARMOR_CMD_UNLOAD, c)
}

func AADestroy ( c container ) error { state := c . DaemonState ( ) if ! state . OS . AppArmorAdmin { return nil } if state . OS . AppArmorStacking && ! state . OS . AppArmorStacked { p := path . Join ( " " , AANamespace ( c ) ) if err := os . Remove ( p ) ; err != nil { logger . Error ( " " , log . Ctx { " " : err , " " : p } ) } } return runApparmor ( APPARMOR_CMD_UNLOAD , c ) }

// Ensure that the container's policy namespace is unloaded to free kernel
// memory. This does not delete the policy from disk or cache.

Ensure that the container s policy namespace is unloaded to free kernel memory . This does not delete the policy from disk or cache .


func AAParseProfile(c container) error {
	state := c.DaemonState()
	if !state.OS.AppArmorAvailable {
		return nil
	}

	return runApparmor(APPARMOR_CMD_PARSE, c)
}

func AAParseProfile ( c container ) error { state := c . DaemonState ( ) if ! state . OS . AppArmorAvailable { return nil } return runApparmor ( APPARMOR_CMD_PARSE , c ) }

// Parse the profile without loading it into the kernel.

Parse the profile without loading it into the kernel .


func getSystemHandler(syslog string, debug bool, format log.Format) log.Handler {
	return nil
}

func getSystemHandler ( syslog string , debug bool , format log . Format ) log . Handler { return nil }

// getSystemHandler on Windows does nothing.

getSystemHandler on Windows does nothing .


func NotifyUpgradeCompleted(state *state.State, cert *shared.CertInfo) error {
	notifier, err := NewNotifier(state, cert, NotifyAll)
	if err != nil {
		return err
	}
	return notifier(func(client lxd.ContainerServer) error {
		info, err := client.GetConnectionInfo()
		if err != nil {
			return errors.Wrap(err, "failed to get connection info")
		}

		url := fmt.Sprintf("%s%s", info.Addresses[0], databaseEndpoint)
		request, err := http.NewRequest("PATCH", url, nil)
		if err != nil {
			return errors.Wrap(err, "failed to create database notify upgrade request")
		}

		httpClient, err := client.GetHTTPClient()
		if err != nil {
			return errors.Wrap(err, "failed to get HTTP client")
		}

		response, err := httpClient.Do(request)
		if err != nil {
			return errors.Wrap(err, "failed to notify node about completed upgrade")
		}

		if response.StatusCode != http.StatusOK {
			return fmt.Errorf("database upgrade notification failed: %s", response.Status)
		}

		return nil
	})
}

func NotifyUpgradeCompleted ( state * state . State , cert * shared . CertInfo ) error { notifier , err := NewNotifier ( state , cert , NotifyAll ) if err != nil { return err } return notifier ( func ( client lxd . ContainerServer ) error { info , err := client . GetConnectionInfo ( ) if err != nil { return errors . Wrap ( err , " " ) } url := fmt . Sprintf ( " " , info . Addresses [ 0 ] , databaseEndpoint ) request , err := http . NewRequest ( " " , url , nil ) if err != nil { return errors . Wrap ( err , " " ) } httpClient , err := client . GetHTTPClient ( ) if err != nil { return errors . Wrap ( err , " " ) } response , err := httpClient . Do ( request ) if err != nil { return errors . Wrap ( err , " " ) } if response . StatusCode != http . StatusOK { return fmt . Errorf ( " " , response . Status ) } return nil } ) }

// NotifyUpgradeCompleted sends a notification to all other nodes in the
// cluster that any possible pending database update has been applied, and any
// nodes which was waiting for this node to be upgraded should re-check if it's
// okay to move forward.

NotifyUpgradeCompleted sends a notification to all other nodes in the cluster that any possible pending database update has been applied and any nodes which was waiting for this node to be upgraded should re - check if it s okay to move forward .


func KeepUpdated(state *state.State) (task.Func, task.Schedule) {
	f := func(ctx context.Context) {
		ch := make(chan struct{})
		go func() {
			maybeUpdate(state)
			close(ch)
		}()
		select {
		case <-ctx.Done():
		case <-ch:
		}
	}

	schedule := task.Every(5 * time.Minute)

	return f, schedule
}

func KeepUpdated ( state * state . State ) ( task . Func , task . Schedule ) { f := func ( ctx context . Context ) { ch := make ( chan struct { } ) go func ( ) { maybeUpdate ( state ) close ( ch ) } ( ) select { case <- ctx . Done ( ) : case <- ch : } } schedule := task . Every ( 5 * time . Minute ) return f , schedule }

// KeepUpdated is a task that continuously monitor this node's version to see it
// it's out of date with respect to other nodes. In the node is out of date,
// and the LXD_CLUSTER_UPDATE environment variable is set, then the task
// executes the executable that the variable is pointing at.

KeepUpdated is a task that continuously monitor this node s version to see it it s out of date with respect to other nodes . In the node is out of date and the LXD_CLUSTER_UPDATE environment variable is set then the task executes the executable that the variable is pointing at .


func maybeUpdate(state *state.State) {
	shouldUpdate := false

	enabled, err := Enabled(state.Node)
	if err != nil {
		logger.Errorf("Failed to check clustering is enabled: %v", err)
		return
	}
	if !enabled {
		return
	}

	err = state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		outdated, err := tx.NodeIsOutdated()
		if err != nil {
			return err
		}
		shouldUpdate = outdated
		return nil
	})

	if err != nil {
		// Just log the error and return.
		logger.Errorf("Failed to check if this node is out-of-date: %v", err)
		return
	}

	if !shouldUpdate {
		logger.Debugf("Cluster node is up-to-date")
		return
	}

	logger.Infof("Node is out-of-date with respect to other cluster nodes")

	updateExecutable := os.Getenv("LXD_CLUSTER_UPDATE")
	if updateExecutable == "" {
		logger.Debug("No LXD_CLUSTER_UPDATE variable set, skipping auto-update")
		return
	}

	logger.Infof("Triggering cluster update using: %s", updateExecutable)

	_, err = shared.RunCommand(updateExecutable)
	if err != nil {
		logger.Errorf("Cluster upgrade failed: '%v'", err.Error())
		return
	}
}

func
maybeUpdate
(
state
*
state
.
State
)
{
shouldUpdate
:=
false
enabled
,
err
:=
Enabled
(
state
.
Node
)
if
err
!=
{
logger
.
Errorf
(
"
"
,
err
)
return
}
if
!
enabled
{
return
}
err
=
state
.
Cluster
.
Transaction
(
func
(
tx
*
db
.
ClusterTx
)
error
{
outdated
,
err
:=
tx
.
NodeIsOutdated
(
)
if
err
!=
{
return
err
}
shouldUpdate
=
outdated
return
}
)
if
err
!=
{
// Just log the error and return.
logger
.
Errorf
(
"
"
,
err
)
return
}
if
!
shouldUpdate
{
logger
.
Debugf
(
"
"
)
return
}
logger
.
Infof
(
"
"
)
updateExecutable
:=
os
.
Getenv
(
"
"
)
if
updateExecutable
==
"
"
{
logger
.
Debug
(
"
"
)
return
}
logger
.
Infof
(
"
"
,
updateExecutable
)
_
,
err
=
shared
.
RunCommand
(
updateExecutable
)
if
err
!=
{
logger
.
Errorf
(
"
"
,
err
.
Error
(
)
)
return
}
}

// Check this node's version and possibly run LXD_CLUSTER_UPDATE.

Check this node s version and possibly run LXD_CLUSTER_UPDATE .


func NewServer(apiURL string, apiKey string, agentAuthURL string, agentUsername string, agentPrivateKey string, agentPublicKey string) (*Server, error) {
	r := Server{
		apiURL:          apiURL,
		apiKey:          apiKey,
		lastSyncID:      "",
		lastChange:      time.Time{},
		resources:       make(map[string]string),
		permissions:     make(map[string]map[string][]string),
		permissionsLock: &sync.Mutex{},
	}

	//
	var keyPair bakery.KeyPair
	keyPair.Private.UnmarshalText([]byte(agentPrivateKey))
	keyPair.Public.UnmarshalText([]byte(agentPublicKey))

	r.client = httpbakery.NewClient()
	authInfo := agent.AuthInfo{
		Key: &keyPair,
		Agents: []agent.Agent{
			{
				URL:      agentAuthURL,
				Username: agentUsername,
			},
		},
	}

	err := agent.SetUpAuth(r.client, &authInfo)
	if err != nil {
		return nil, err
	}

	r.client.Client.Jar, err = cookiejar.New(nil)
	if err != nil {
		return nil, err
	}

	return &r, nil
}

func NewServer ( apiURL string , apiKey string , agentAuthURL string , agentUsername string , agentPrivateKey string , agentPublicKey string ) ( * Server , error ) { r := Server { apiURL : apiURL , apiKey : apiKey , lastSyncID : " " , lastChange : time . Time { } , resources : make ( map [ string ] string ) , permissions : make ( map [ string ] map [ string ] [ ] string ) , permissionsLock : & sync . Mutex { } , } // var keyPair bakery . KeyPair keyPair . Private . UnmarshalText ( [ ] byte ( agentPrivateKey ) ) keyPair . Public . UnmarshalText ( [ ] byte ( agentPublicKey ) ) r . client = httpbakery . NewClient ( ) authInfo := agent . AuthInfo { Key : & keyPair , Agents : [ ] agent . Agent { { URL : agentAuthURL , Username : agentUsername , } , } , } err := agent . SetUpAuth ( r . client , & authInfo ) if err != nil { return nil , err } r . client . Client . Jar , err = cookiejar . New ( nil ) if err != nil { return nil , err } return & r , nil }

// NewServer returns a new RBAC server instance.

NewServer returns a new RBAC server instance .


func (r *Server) StartStatusCheck() {
	// Initialize the last changed timestamp
	r.hasStatusChanged()

	r.statusDone = make(chan int)
	go func() {
		for {
			select {
			case <-r.statusDone:
				return
			case <-time.After(time.Minute):
				if r.hasStatusChanged() {
					r.flushCache()
				}
			}
		}
	}()
}

// StartStatusCheck starts a periodic status checker.

StartStatusCheck starts a periodic status checker .


func (r *Server) SyncProjects() error {
	if r.ProjectsFunc == nil {
		return fmt.Errorf("ProjectsFunc isn't configured yet, cannot sync")
	}

	resources := []rbacResource{}
	resourcesMap := map[string]string{}

	// Get all projects
	projects, err := r.ProjectsFunc()
	if err != nil {
		return err
	}

	// Convert to RBAC format
	for id, name := range projects {
		resources = append(resources, rbacResource{
			Name:       name,
			Identifier: strconv.FormatInt(id, 10),
		})

		resourcesMap[name] = strconv.FormatInt(id, 10)
	}

	// Update RBAC
	err = r.postResources(resources, nil, true)
	if err != nil {
		return err
	}

	// Update project map
	r.resourcesLock.Lock()
	r.resources = resourcesMap
	r.resourcesLock.Unlock()

	return nil
}

func
(
r
*
Server
)
SyncProjects
(
)
error
{
if
r
.
ProjectsFunc
==
{
return
fmt
.
Errorf
(
"
"
)
}
resources
:=
[
]
rbacResource
{
}
resourcesMap
:=
map
[
string
]
string
{
}
// Get all projects
projects
,
err
:=
r
.
ProjectsFunc
(
)
if
err
!=
{
return
err
}
// Convert to RBAC format
for
id
,
name
:=
range
projects
{
resources
=
append
(
resources
,
rbacResource
{
Name
:
name
,
Identifier
:
strconv
.
FormatInt
(
id
,
10
)
,
}
)
resourcesMap
[
name
]
=
strconv
.
FormatInt
(
id
,
10
)
}
// Update RBAC
err
=
r
.
postResources
(
resources
,
,
true
)
if
err
!=
{
return
err
}
// Update project map
r
.
resourcesLock
.
Lock
(
)
r
.
resources
=
resourcesMap
r
.
resourcesLock
.
Unlock
(
)
return
}

// SyncProjects updates the list of projects in RBAC


func (r *Server) AddProject(id int64, name string) error {
	resource := rbacResource{
		Name:       name,
		Identifier: strconv.FormatInt(id, 10),
	}

	// Update RBAC
	err := r.postResources([]rbacResource{resource}, nil, false)
	if err != nil {
		return err
	}

	// Update project map
	r.resourcesLock.Lock()
	r.resources[name] = strconv.FormatInt(id, 10)
	r.resourcesLock.Unlock()

	return nil
}

func
(
r
*
Server
)
AddProject
(
id
int64
,
name
string
)
error
{
resource
:=
rbacResource
{
Name
:
name
,
Identifier
:
strconv
.
FormatInt
(
id
,
10
)
,
}
// Update RBAC
err
:=
r
.
postResources
(
[
]
rbacResource
{
resource
}
,
,
false
)
if
err
!=
{
return
err
}
// Update project map
r
.
resourcesLock
.
Lock
(
)
r
.
resources
[
name
]
=
strconv
.
FormatInt
(
id
,
10
)
r
.
resourcesLock
.
Unlock
(
)
return
}

// AddProject adds a new project resource to RBAC.

AddProject adds a new project resource to RBAC .


func (r *Server) DeleteProject(id int64) error {
	// Update RBAC
	err := r.postResources(nil, []string{strconv.FormatInt(id, 10)}, false)
	if err != nil {
		return err
	}

	// Update project map
	r.resourcesLock.Lock()
	for k, v := range r.resources {
		if v == strconv.FormatInt(id, 10) {
			delete(r.resources, k)
			break
		}
	}
	r.resourcesLock.Unlock()

	return nil
}

func
(
r
*
Server
)
DeleteProject
(
id
int64
)
error
{
// Update RBAC
err
:=
r
.
postResources
(
,
[
]
string
{
strconv
.
FormatInt
(
id
,
10
)
}
,
false
)
if
err
!=
{
return
err
}
// Update project map
r
.
resourcesLock
.
Lock
(
)
for
k
,
v
:=
range
r
.
resources
{
if
v
==
strconv
.
FormatInt
(
id
,
10
)
{
delete
(
r
.
resources
,
k
)
break
}
}
r
.
resourcesLock
.
Unlock
(
)
return
}

// DeleteProject adds a new project resource to RBAC.

DeleteProject adds a new project resource to RBAC .


func (r *Server) RenameProject(id int64, name string) error {
	return r.AddProject(id, name)
}

func ( r * Server ) RenameProject ( id int64 , name string ) error { return r . AddProject ( id , name ) }

// RenameProject renames an existing project resource in RBAC.

RenameProject renames an existing project resource in RBAC .


func (r *Server) IsAdmin(username string) bool {
	r.permissionsLock.Lock()
	defer r.permissionsLock.Unlock()

	// Check whether the permissions are cached
	_, cached := r.permissions[username]

	if !cached {
		r.syncPermissions(username)
	}

	return shared.StringInSlice("admin", r.permissions[username][""])
}

// IsAdmin returns whether or not the provided user is an admin.

IsAdmin returns whether or not the provided user is an admin .


func (r *Server) HasPermission(username, project, permission string) bool {
	r.permissionsLock.Lock()
	defer r.permissionsLock.Unlock()

	// Check whether the permissions are cached
	_, cached := r.permissions[username]

	if !cached {
		r.syncPermissions(username)
	}

	r.resourcesLock.Lock()
	permissions := r.permissions[username][r.resources[project]]
	r.resourcesLock.Unlock()

	return shared.StringInSlice(permission, permissions)
}

// HasPermission returns whether or not the user has the permission to perform a certain task.

HasPermission returns whether or not the user has the permission to perform a certain task .


func rsyncSend(conn *websocket.Conn, path string, rsyncArgs string) error {
	cmd, dataSocket, stderr, err := rsyncSendSetup(path, rsyncArgs)
	if err != nil {
		return err
	}

	if dataSocket != nil {
		defer dataSocket.Close()
	}

	readDone, writeDone := shared.WebsocketMirror(conn, dataSocket, io.ReadCloser(dataSocket), nil, nil)

	output, err := ioutil.ReadAll(stderr)
	if err != nil {
		cmd.Process.Kill()
		cmd.Wait()
		return fmt.Errorf("Failed to rsync: %v\n%s", err, output)
	}

	err = cmd.Wait()
	<-readDone
	<-writeDone

	if err != nil {
		return fmt.Errorf("Failed to rsync: %v\n%s", err, output)
	}

	return nil
}

func rsyncSend ( conn * websocket . Conn , path string , rsyncArgs string ) error { cmd , dataSocket , stderr , err := rsyncSendSetup ( path , rsyncArgs ) if err != nil { return err } if dataSocket != nil { defer dataSocket . Close ( ) } readDone , writeDone := shared . WebsocketMirror ( conn , dataSocket , io . ReadCloser ( dataSocket ) , nil , nil ) output , err := ioutil . ReadAll ( stderr ) if err != nil { cmd . Process . Kill ( ) cmd . Wait ( ) return fmt . Errorf ( " \n " , err , output ) } err = cmd . Wait ( ) <- readDone <- writeDone if err != nil { return fmt . Errorf ( " \n " , err , output ) } return nil }

// Send an rsync stream of a path over a websocket


func rsyncSendSetup(path string, rsyncArgs string) (*exec.Cmd, net.Conn, io.ReadCloser, error) {
	auds := fmt.Sprintf("@lxd-p2c/%s", uuid.NewRandom().String())
	if len(auds) > shared.ABSTRACT_UNIX_SOCK_LEN-1 {
		auds = auds[:shared.ABSTRACT_UNIX_SOCK_LEN-1]
	}

	l, err := net.Listen("unix", auds)
	if err != nil {
		return nil, nil, nil, err
	}

	execPath, err := os.Readlink("/proc/self/exe")
	if err != nil {
		return nil, nil, nil, err
	}

	if !shared.PathExists(execPath) {
		execPath = os.Args[0]
	}

	rsyncCmd := fmt.Sprintf("sh -c \"%s netcat %s\"", execPath, auds)

	args := []string{
		"-ar",
		"--devices",
		"--numeric-ids",
		"--partial",
		"--sparse",
		"--xattrs",
		"--delete",
		"--compress",
		"--compress-level=2",
	}

	// Ignore deletions (requires 3.1 or higher)
	rsyncCheckVersion := func(min string) bool {
		out, err := shared.RunCommand("rsync", "--version")
		if err != nil {
			return false
		}

		fields := strings.Split(out, " ")
		curVer, err := version.Parse(fields[3])
		if err != nil {
			return false
		}

		minVer, err := version.Parse(min)
		if err != nil {
			return false
		}

		return curVer.Compare(minVer) >= 0
	}

	if rsyncCheckVersion("3.1.0") {
		args = append(args, "--ignore-missing-args")
	}

	if rsyncArgs != "" {
		args = append(args, strings.Split(rsyncArgs, " ")...)
	}

	args = append(args, []string{path, "localhost:/tmp/foo"}...)
	args = append(args, []string{"-e", rsyncCmd}...)

	cmd := exec.Command("rsync", args...)
	cmd.Stdout = os.Stderr

	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil, nil, nil, err
	}

	if err := cmd.Start(); err != nil {
		return nil, nil, nil, err
	}

	conn, err := l.Accept()
	if err != nil {
		cmd.Process.Kill()
		cmd.Wait()
		return nil, nil, nil, err
	}
	l.Close()

	return cmd, conn, stderr, nil
}


func tlsClientConfig(info *shared.CertInfo) (*tls.Config, error) {
	keypair := info.KeyPair()
	ca := info.CA()
	config := shared.InitTLSConfig()
	config.Certificates = []tls.Certificate{keypair}
	config.RootCAs = x509.NewCertPool()
	if ca != nil {
		config.RootCAs.AddCert(ca)
	}
	// Since the same cluster keypair is used both as server and as client
	// cert, let's add it to the CA pool to make it trusted.
	cert, err := x509.ParseCertificate(keypair.Certificate[0])
	if err != nil {
		return nil, err
	}
	cert.IsCA = true
	cert.KeyUsage = x509.KeyUsageCertSign
	config.RootCAs.AddCert(cert)

	if cert.DNSNames != nil {
		config.ServerName = cert.DNSNames[0]
	}
	return config, nil
}

// Return a TLS configuration suitable for establishing inter-node network
// connections using the cluster certificate.

Return a TLS configuration suitable for establishing inter - node network connections using the cluster certificate .


func tlsCheckCert(r *http.Request, info *shared.CertInfo) bool {
	cert, err := x509.ParseCertificate(info.KeyPair().Certificate[0])
	if err != nil {
		// Since we have already loaded this certificate, typically
		// using LoadX509KeyPair, an error should never happen, but
		// check for good measure.
		panic(fmt.Sprintf("invalid keypair material: %v", err))
	}
	trustedCerts := map[string]x509.Certificate{"0": *cert}

	trusted, _ := util.CheckTrustState(*r.TLS.PeerCertificates[0], trustedCerts)

	return r.TLS != nil && trusted
}

// Return true if the given request is presenting the given cluster certificate.

Return true if the given request is presenting the given cluster certificate .


func internalClusterContainerMovedPost(d *Daemon, r *http.Request) Response {
	project := projectParam(r)
	containerName := mux.Vars(r)["name"]
	err := containerPostCreateContainerMountPoint(d, project, containerName)
	if err != nil {
		return SmartError(err)
	}
	return EmptySyncResponse
}

func internalClusterContainerMovedPost ( d * Daemon , r * http . Request ) Response { project := projectParam ( r ) containerName := mux . Vars ( r ) [ " " ] err := containerPostCreateContainerMountPoint ( d , project , containerName ) if err != nil { return SmartError ( err ) } return EmptySyncResponse }

// Notification that a container was moved.
//
// At the moment it's used for ceph-based containers, where the target node needs
// to create the appropriate mount points.

Notification that a container was moved . At the moment it s used for ceph - based containers where the target node needs to create the appropriate mount points .


func containerPostCreateContainerMountPoint(d *Daemon, project, containerName string) error {
	c, err := containerLoadByProjectAndName(d.State(), project, containerName)
	if err != nil {
		return errors.Wrap(err, "Failed to load moved container on target node")
	}
	poolName, err := c.StoragePool()
	if err != nil {
		return errors.Wrap(err, "Failed get pool name of moved container on target node")
	}
	snapshotNames, err := d.cluster.ContainerGetSnapshots(project, containerName)
	if err != nil {
		return errors.Wrap(err, "Failed to create container snapshot names")
	}

	containerMntPoint := getContainerMountPoint(c.Project(), poolName, containerName)
	err = createContainerMountpoint(containerMntPoint, c.Path(), c.IsPrivileged())
	if err != nil {
		return errors.Wrap(err, "Failed to create container mount point on target node")
	}

	for _, snapshotName := range snapshotNames {
		mntPoint := getSnapshotMountPoint(project, poolName, snapshotName)
		snapshotsSymlinkTarget := shared.VarPath("storage-pools",
			poolName, "containers-snapshots", containerName)
		snapshotMntPointSymlink := shared.VarPath("snapshots", containerName)
		err := createSnapshotMountpoint(mntPoint, snapshotsSymlinkTarget, snapshotMntPointSymlink)
		if err != nil {
			return errors.Wrap(err, "Failed to create snapshot mount point on target node")
		}
	}

	return nil
}

func containerPostCreateContainerMountPoint ( d * Daemon , project , containerName string ) error { c , err := containerLoadByProjectAndName ( d . State ( ) , project , containerName ) if err != nil { return errors . Wrap ( err , " " ) } poolName , err := c . StoragePool ( ) if err != nil { return errors . Wrap ( err , " " ) } snapshotNames , err := d . cluster . ContainerGetSnapshots ( project , containerName ) if err != nil { return errors . Wrap ( err , " " ) } containerMntPoint := getContainerMountPoint ( c . Project ( ) , poolName , containerName ) err = createContainerMountpoint ( containerMntPoint , c . Path ( ) , c . IsPrivileged ( ) ) if err != nil { return errors . Wrap ( err , " " ) } for _ , snapshotName := range snapshotNames { mntPoint := getSnapshotMountPoint ( project , poolName , snapshotName ) snapshotsSymlinkTarget := shared . VarPath ( " " , poolName , " " , containerName ) snapshotMntPointSymlink := shared . VarPath ( " " , containerName ) err := createSnapshotMountpoint ( mntPoint , snapshotsSymlinkTarget , snapshotMntPointSymlink ) if err != nil { return errors . Wrap ( err , " " ) } } return nil }

// Used after to create the appropriate mounts point after a container has been
// moved.

Used after to create the appropriate mounts point after a container has been moved .


func (list Devices) Contains(k string, d Device) bool {
	// If it didn't exist, it's different
	if list[k] == nil {
		return false
	}

	old := list[k]

	return deviceEquals(old, d)
}

func
(
list
Devices
)
Contains
(
k
string
,
d
Device
)
bool
{
// If it didn't exist, it's different
if
list
[
k
]
==
{
return
false
}
old
:=
list
[
k
]
return
deviceEquals
(
old
,
d
)
}

// Contains checks if a given device exists in the set and if it's
// identical to that provided

Contains checks if a given device exists in the set and if it s identical to that provided


func (list Devices) Update(newlist Devices) (map[string]Device, map[string]Device, map[string]Device, []string) {
	rmlist := map[string]Device{}
	addlist := map[string]Device{}
	updatelist := map[string]Device{}

	for key, d := range list {
		if !newlist.Contains(key, d) {
			rmlist[key] = d
		}
	}

	for key, d := range newlist {
		if !list.Contains(key, d) {
			addlist[key] = d
		}
	}

	updateDiff := []string{}
	for key, d := range addlist {
		srcOldDevice := rmlist[key]
		var oldDevice Device
		err := shared.DeepCopy(&srcOldDevice, &oldDevice)
		if err != nil {
			continue
		}

		srcNewDevice := newlist[key]
		var newDevice Device
		err = shared.DeepCopy(&srcNewDevice, &newDevice)
		if err != nil {
			continue
		}

		updateDiff = deviceEqualsDiffKeys(oldDevice, newDevice)

		for _, k := range []string{"limits.max", "limits.read", "limits.write", "limits.egress", "limits.ingress", "ipv4.address", "ipv6.address", "ipv4.routes", "ipv6.routes"} {
			delete(oldDevice, k)
			delete(newDevice, k)
		}

		if deviceEquals(oldDevice, newDevice) {
			delete(rmlist, key)
			delete(addlist, key)
			updatelist[key] = d
		}
	}

	return rmlist, addlist, updatelist, updateDiff
}

func ( list Devices ) Update ( newlist Devices ) ( map [ string ] Device , map [ string ] Device , map [ string ] Device , [ ] string ) { rmlist := map [ string ] Device { } addlist := map [ string ] Device { } updatelist := map [ string ] Device { } for key , d := range list { if ! newlist . Contains ( key , d ) { rmlist [ key ] = d } } for key , d := range newlist { if ! list . Contains ( key , d ) { addlist [ key ] = d } } updateDiff := [ ] string { } for key , d := range addlist { srcOldDevice := rmlist [ key ] var oldDevice Device err := shared . DeepCopy ( & srcOldDevice , & oldDevice ) if err != nil { continue } srcNewDevice := newlist [ key ] var newDevice Device err = shared . DeepCopy ( & srcNewDevice , & newDevice ) if err != nil { continue } updateDiff = deviceEqualsDiffKeys ( oldDevice , newDevice ) for _ , k := range [ ] string { " " , " " , " " , " " , " " , " " , " " , " " , " " } { delete ( oldDevice , k ) delete ( newDevice , k ) } if deviceEquals ( oldDevice , newDevice ) { delete ( rmlist , key ) delete ( addlist , key ) updatelist [ key ] = d } } return rmlist , addlist , updatelist , updateDiff }

// Update returns the difference between two sets


func (list Devices) DeviceNames() []string {
	sortable := sortableDevices{}
	for k, d := range list {
		sortable = append(sortable, namedDevice{k, d})
	}

	sort.Sort(sortable)
	return sortable.Names()
}

func ( list Devices ) DeviceNames ( ) [ ] string { sortable := sortableDevices { } for k , d := range list { sortable = append ( sortable , namedDevice { k , d } ) } sort . Sort ( sortable ) return sortable . Names ( ) }

// DeviceNames returns the name of all devices in the set, sorted properly

DeviceNames returns the name of all devices in the set sorted properly


func Infof(format string, args ...interface{}) {
	if Log != nil {
		Log.Info(fmt.Sprintf(format, args...))
	}
}

func Infof ( format string , args ... interface { } ) { if Log != nil { Log . Info ( fmt . Sprintf ( format , args ... ) ) } }

// Infof logs at the INFO log level using a standard printf format string


func Debugf(format string, args ...interface{}) {
	if Log != nil {
		Log.Debug(fmt.Sprintf(format, args...))
	}
}

func Debugf ( format string , args ... interface { } ) { if Log != nil { Log . Debug ( fmt . Sprintf ( format , args ... ) ) } }

// Debugf logs at the DEBUG log level using a standard printf format string


func Warnf(format string, args ...interface{}) {
	if Log != nil {
		Log.Warn(fmt.Sprintf(format, args...))
	}
}

func Warnf ( format string , args ... interface { } ) { if Log != nil { Log . Warn ( fmt . Sprintf ( format , args ... ) ) } }

// Warnf logs at the WARNING log level using a standard printf format string


func Errorf(format string, args ...interface{}) {
	if Log != nil {
		Log.Error(fmt.Sprintf(format, args...))
	}
}

func Errorf ( format string , args ... interface { } ) { if Log != nil { Log . Error ( fmt . Sprintf ( format , args ... ) ) } }

// Errorf logs at the ERROR log level using a standard printf format string


func Critf(format string, args ...interface{}) {
	if Log != nil {
		Log.Crit(fmt.Sprintf(format, args...))
	}
}

func Critf ( format string , args ... interface { } ) { if Log != nil { Log . Crit ( fmt . Sprintf ( format , args ... ) ) } }

// Critf logs at the CRITICAL log level using a standard printf format string


func eventForward(id int64, event api.Event) {
	if event.Type == "logging" {
		// Parse the message
		logEntry := api.EventLogging{}
		err := json.Unmarshal(event.Metadata, &logEntry)
		if err != nil {
			return
		}

		if !debug && logEntry.Level == "dbug" {
			return
		}

		if !debug && !verbose && logEntry.Level == "info" {
			return
		}
	}

	err := eventBroadcast("", event, true)
	if err != nil {
		logger.Warnf("Failed to forward event from node %d: %v", id, err)
	}
}

func
eventForward
(
id
int64
,
event
api
.
Event
)
{
if
event
.
Type
==
"
"
{
// Parse the message
logEntry
:=
api
.
EventLogging
{
}
err
:=
json
.
Unmarshal
(
event
.
Metadata
,
&
logEntry
)
if
err
!=
{
return
}
if
!
debug
&&
logEntry
.
Level
==
"
"
{
return
}
if
!
debug
&&
!
verbose
&&
logEntry
.
Level
==
"
"
{
return
}
}
err
:=
eventBroadcast
(
"
"
,
event
,
true
)
if
err
!=
{
logger
.
Warnf
(
"
"
,
id
,
err
)
}
}

// Forward to the local events dispatcher an event received from another node .


func StorageProgressReader(op *operation, key string, description string) func(io.ReadCloser) io.ReadCloser {
	return func(reader io.ReadCloser) io.ReadCloser {
		if op == nil {
			return reader
		}

		progress := func(progressInt int64, speedInt int64) {
			progressWrapperRender(op, key, description, progressInt, speedInt)
		}

		readPipe := &ioprogress.ProgressReader{
			ReadCloser: reader,
			Tracker: &ioprogress.ProgressTracker{
				Handler: progress,
			},
		}

		return readPipe
	}
}

func StorageProgressReader ( op * operation , key string , description string ) func ( io . ReadCloser ) io . ReadCloser { return func ( reader io . ReadCloser ) io . ReadCloser { if op == nil { return reader } progress := func ( progressInt int64 , speedInt int64 ) { progressWrapperRender ( op , key , description , progressInt , speedInt ) } readPipe := & ioprogress . ProgressReader { ReadCloser : reader , Tracker : & ioprogress . ProgressTracker { Handler : progress , } , } return readPipe } }

// StorageProgressReader reports the read progress.

StorageProgressReader reports the read progress .


func StorageProgressWriter(op *operation, key string, description string) func(io.WriteCloser) io.WriteCloser {
	return func(writer io.WriteCloser) io.WriteCloser {
		if op == nil {
			return writer
		}

		progress := func(progressInt int64, speedInt int64) {
			progressWrapperRender(op, key, description, progressInt, speedInt)
		}

		writePipe := &ioprogress.ProgressWriter{
			WriteCloser: writer,
			Tracker: &ioprogress.ProgressTracker{
				Handler: progress,
			},
		}

		return writePipe
	}
}

func StorageProgressWriter ( op * operation , key string , description string ) func ( io . WriteCloser ) io . WriteCloser { return func ( writer io . WriteCloser ) io . WriteCloser { if op == nil { return writer } progress := func ( progressInt int64 , speedInt int64 ) { progressWrapperRender ( op , key , description , progressInt , speedInt ) } writePipe := & ioprogress . ProgressWriter { WriteCloser : writer , Tracker : & ioprogress . ProgressTracker { Handler : progress , } , } return writePipe } }

// StorageProgressWriter reports the write progress.

StorageProgressWriter reports the write progress .


func GetLSBRelease() (map[string]string, error) {
	osRelease, err := getLSBRelease("/etc/os-release")
	if os.IsNotExist(err) {
		return getLSBRelease("/usr/lib/os-release")
	}
	return osRelease, err
}

func GetLSBRelease ( ) ( map [ string ] string , error ) { osRelease , err := getLSBRelease ( " " ) if os . IsNotExist ( err ) { return getLSBRelease ( " " ) } return osRelease , err }

// GetLSBRelease returns a map with Linux distribution information


func Reset(path string, imports []string) error {
	content := fmt.Sprintf(`package %s

// The code below was generated by %s - DO NOT EDIT!

import (
`, os.Getenv("GOPACKAGE"), os.Args[0])

	for _, uri := range imports {
		content += fmt.Sprintf("\t%q\n", uri)
	}

	content += ")\n\n"

	// FIXME: we should only import what's needed.
	content += "var _ = api.ServerEnvironment{}\n"

	bytes := []byte(content)

	var err error

	if path == "-" {
		_, err = os.Stdout.Write(bytes)
	} else {
		err = ioutil.WriteFile(path, []byte(content), 0644)
	}

	if err != nil {
		errors.Wrapf(err, "Reset target source file '%s'", path)
	}

	return nil
}

// Reset an auto-generated source file, writing a new empty file header.

Reset an auto - generated source file writing a new empty file header .


func Append(path string, snippet Snippet) error {
	buffer := newBuffer()
	buffer.N()

	err := snippet.Generate(buffer)
	if err != nil {
		return errors.Wrap(err, "Generate code snippet")
	}

	var file *os.File

	if path == "-" {
		file = os.Stdout
	} else {
		file, err = os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
		if err != nil {
			return errors.Wrapf(err, "Open target source code file '%s'", path)
		}
		defer file.Close()
	}

	bytes, err := buffer.code()
	if err != nil {
		return err
	}

	_, err = file.Write(bytes)
	if err != nil {
		return errors.Wrapf(err, "Append snippet to target source code file '%s'", path)
	}

	return nil
}

func Append ( path string , snippet Snippet ) error { buffer := newBuffer ( ) buffer . N ( ) err := snippet . Generate ( buffer ) if err != nil { return errors . Wrap ( err , " " ) } var file * os . File if path == " " { file = os . Stdout } else { file , err = os . OpenFile ( path , os . O_APPEND | os . O_WRONLY , 0644 ) if err != nil { return errors . Wrapf ( err , " " , path ) } defer file . Close ( ) } bytes , err := buffer . code ( ) if err != nil { return err } _ , err = file . Write ( bytes ) if err != nil { return errors . Wrapf ( err , " " , path ) } return nil }


func ContainerToArgs(container *Container) ContainerArgs {
	args := ContainerArgs{
		ID:           container.ID,
		Project:      container.Project,
		Name:         container.Name,
		Node:         container.Node,
		Ctype:        ContainerType(container.Type),
		Architecture: container.Architecture,
		Ephemeral:    container.Ephemeral,
		CreationDate: container.CreationDate,
		Stateful:     container.Stateful,
		LastUsedDate: container.LastUseDate,
		Description:  container.Description,
		Config:       container.Config,
		Devices:      container.Devices,
		Profiles:     container.Profiles,
		ExpiryDate:   container.ExpiryDate,
	}

	if args.Devices == nil {
		args.Devices = types.Devices{}
	}

	return args
}

func ContainerToArgs ( container * Container ) ContainerArgs { args := ContainerArgs { ID : container . ID , Project : container . Project , Name : container . Name , Node : container . Node , Ctype : ContainerType ( container . Type ) , Architecture : container . Architecture , Ephemeral : container . Ephemeral , CreationDate : container . CreationDate , Stateful : container . Stateful , LastUsedDate : container . LastUseDate , Description : container . Description , Config : container . Config , Devices : container . Devices , Profiles : container . Profiles , ExpiryDate : container . ExpiryDate , } if args . Devices == nil { args . Devices = types . Devices { } } return args }

// ContainerToArgs is a convenience to convert the new Container db struct into
// the legacy ContainerArgs.

ContainerToArgs is a convenience to convert the new Container db struct into the legacy ContainerArgs .


func (c *ClusterTx) ContainerNames(project string) ([]string, error) {
	stmt := `
SELECT containers.name FROM containers
  JOIN projects ON projects.id = containers.project_id
  WHERE projects.name = ? AND containers.type = ?
`
	return query.SelectStrings(c.tx, stmt, project, CTypeRegular)
}

// ContainerNames returns the names of all containers the given project.

ContainerNames returns the names of all containers the given project .


func (c *ClusterTx) ContainerNodeAddress(project string, name string) (string, error) {
	stmt := `
SELECT nodes.id, nodes.address
  FROM nodes
  JOIN containers ON containers.node_id = nodes.id
  JOIN projects ON projects.id = containers.project_id
 WHERE projects.name = ? AND containers.name = ?
`
	var address string
	var id int64
	rows, err := c.tx.Query(stmt, project, name)
	if err != nil {
		return "", err
	}
	defer rows.Close()

	if !rows.Next() {
		return "", ErrNoSuchObject
	}

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

	if rows.Next() {
		return "", fmt.Errorf("more than one node associated with container")
	}

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

	if id == c.nodeID {
		return "", nil
	}

	return address, nil
}

// ContainerNodeAddress returns the address of the node hosting the container
// with the given name in the given project.
//
// It returns the empty string if the container is hosted on this node.

ContainerNodeAddress returns the address of the node hosting the container with the given name in the given project . It returns the empty string if the container is hosted on this node .


func (c *ClusterTx) ContainersListByNodeAddress(project string) (map[string][]string, error) {
	offlineThreshold, err := c.NodeOfflineThreshold()
	if err != nil {
		return nil, err
	}

	stmt := `
SELECT containers.name, nodes.id, nodes.address, nodes.heartbeat
  FROM containers
  JOIN nodes ON nodes.id = containers.node_id
  JOIN projects ON projects.id = containers.project_id
  WHERE containers.type=?
    AND projects.name = ?
  ORDER BY containers.id
`
	rows, err := c.tx.Query(stmt, CTypeRegular, project)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	result := map[string][]string{}

	for i := 0; rows.Next(); i++ {
		var name string
		var nodeAddress string
		var nodeID int64
		var nodeHeartbeat time.Time
		err := rows.Scan(&name, &nodeID, &nodeAddress, &nodeHeartbeat)
		if err != nil {
			return nil, err
		}
		if nodeID == c.nodeID {
			nodeAddress = ""
		} else if nodeIsOffline(offlineThreshold, nodeHeartbeat) {
			nodeAddress = "0.0.0.0"
		}
		result[nodeAddress] = append(result[nodeAddress], name)
	}

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

// ContainersListByNodeAddress returns the names of all containers grouped by
// cluster node address.
//
// The node address of containers running on the local node is set to the empty
// string, to distinguish it from remote nodes.
//
// Containers whose node is down are addeded to the special address "0.0.0.0".

ContainersListByNodeAddress returns the names of all containers grouped by cluster node address . The node address of containers running on the local node is set to the empty string to distinguish it from remote nodes . Containers whose node is down are addeded to the special address 0 . 0 . 0 . 0 .


func (c *ClusterTx) ContainerListExpanded() ([]Container, error) {
	containers, err := c.ContainerList(ContainerFilter{})
	if err != nil {
		return nil, errors.Wrap(err, "Load containers")
	}

	profiles, err := c.ProfileList(ProfileFilter{})
	if err != nil {
		return nil, errors.Wrap(err, "Load profiles")
	}

	// Index of all profiles by project and name.
	profilesByProjectAndName := map[string]map[string]Profile{}
	for _, profile := range profiles {
		profilesByName, ok := profilesByProjectAndName[profile.Project]
		if !ok {
			profilesByName = map[string]Profile{}
			profilesByProjectAndName[profile.Project] = profilesByName
		}
		profilesByName[profile.Name] = profile
	}

	for i, container := range containers {
		profiles := make([]api.Profile, len(container.Profiles))
		for j, name := range container.Profiles {
			profile := profilesByProjectAndName[container.Project][name]
			profiles[j] = *ProfileToAPI(&profile)
		}

		containers[i].Config = ProfilesExpandConfig(container.Config, profiles)
		containers[i].Devices = ProfilesExpandDevices(container.Devices, profiles)
	}

	return containers, nil
}

// ContainerListExpanded loads all containers across all projects and expands
// their config and devices using the profiles they are associated to.

ContainerListExpanded loads all containers across all projects and expands their config and devices using the profiles they are associated to .


func (c *ClusterTx) ContainersByNodeName(project string) (map[string]string, error) {
	stmt := `
SELECT containers.name, nodes.name
  FROM containers
  JOIN nodes ON nodes.id = containers.node_id
  JOIN projects ON projects.id = containers.project_id
  WHERE containers.type=?
    AND projects.name = ?
`
	rows, err := c.tx.Query(stmt, CTypeRegular, project)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	result := map[string]string{}

	for i := 0; rows.Next(); i++ {
		var name string
		var nodeName string
		err := rows.Scan(&name, &nodeName)
		if err != nil {
			return nil, err
		}
		result[name] = nodeName
	}

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

// ContainersByNodeName returns a map associating each container to the name of
// its node.

ContainersByNodeName returns a map associating each container to the name of its node .


func (c *ClusterTx) SnapshotIDsAndNames(name string) (map[int]string, error) {
	prefix := name + shared.SnapshotDelimiter
	length := len(prefix)
	objects := make([]struct {
		ID   int
		Name string
	}, 0)
	dest := func(i int) []interface{} {
		objects = append(objects, struct {
			ID   int
			Name string
		}{})
		return []interface{}{&objects[i].ID, &objects[i].Name}
	}
	stmt, err := c.tx.Prepare("SELECT id, name FROM containers WHERE SUBSTR(name,1,?)=? AND type=?")
	if err != nil {
		return nil, err
	}
	defer stmt.Close()
	err = query.SelectObjects(stmt, dest, length, prefix, CTypeSnapshot)
	if err != nil {
		return nil, err
	}
	result := make(map[int]string)
	for i := range objects {
		result[objects[i].ID] = strings.Split(objects[i].Name, shared.SnapshotDelimiter)[1]
	}
	return result, nil
}

func ( c * ClusterTx ) SnapshotIDsAndNames ( name string ) ( map [ int ] string , error ) { prefix := name + shared . SnapshotDelimiter length := len ( prefix ) objects := make ( [ ] struct { ID int Name string } , 0 ) dest := func ( i int ) [ ] interface { } { objects = append ( objects , struct { ID int Name string } { } ) return [ ] interface { } { & objects [ i ] . ID , & objects [ i ] . Name } } stmt , err := c . tx . Prepare ( " " ) if err != nil { return nil , err } defer stmt . Close ( ) err = query . SelectObjects ( stmt , dest , length , prefix , CTypeSnapshot ) if err != nil { return nil , err } result := make ( map [ int ] string ) for i := range objects { result [ objects [ i ] . ID ] = strings . Split ( objects [ i ] . Name , shared . SnapshotDelimiter ) [ 1 ] } return result , nil }

// SnapshotIDsAndNames returns a map of snapshot IDs to snapshot names for the
// container with the given name.

SnapshotIDsAndNames returns a map of snapshot IDs to snapshot names for the container with the given name .


func (c *ClusterTx) ContainerNodeList() ([]Container, error) {
	node, err := c.NodeName()
	if err != nil {
		return nil, errors.Wrap(err, "Local node name")
	}
	filter := ContainerFilter{
		Node: node,
		Type: int(CTypeRegular),
	}

	return c.ContainerList(filter)
}

func ( c * ClusterTx ) ContainerNodeList ( ) ( [ ] Container , error ) { node , err := c . NodeName ( ) if err != nil { return nil , errors . Wrap ( err , " " ) } filter := ContainerFilter { Node : node , Type : int ( CTypeRegular ) , } return c . ContainerList ( filter ) }

// ContainerNodeList returns all container objects on the local node.

ContainerNodeList returns all container objects on the local node .


func (c *ClusterTx) ContainerNodeProjectList(project string) ([]Container, error) {
	node, err := c.NodeName()
	if err != nil {
		return nil, errors.Wrap(err, "Local node name")
	}
	filter := ContainerFilter{
		Project: project,
		Node:    node,
		Type:    int(CTypeRegular),
	}

	return c.ContainerList(filter)
}

func ( c * ClusterTx ) ContainerNodeProjectList ( project string ) ( [ ] Container , error ) { node , err := c . NodeName ( ) if err != nil { return nil , errors . Wrap ( err , " " ) } filter := ContainerFilter { Project : project , Node : node , Type : int ( CTypeRegular ) , } return c . ContainerList ( filter ) }

// ContainerNodeProjectList returns all container objects on the local node within the given project.

ContainerNodeProjectList returns all container objects on the local node within the given project .


func (c *Cluster) ContainerRemove(project, name string) error {
	return c.Transaction(func(tx *ClusterTx) error {
		return tx.ContainerDelete(project, name)
	})
}

func ( c * Cluster ) ContainerRemove ( project , name string ) error { return c . Transaction ( func ( tx * ClusterTx ) error { return tx . ContainerDelete ( project , name ) } ) }

// ContainerRemove removes the container with the given name from the database.

ContainerRemove removes the container with the given name from the database .


func (c *Cluster) ContainerProjectAndName(id int) (string, string, error) {
	q := `
SELECT projects.name, containers.name
  FROM containers
  JOIN projects ON projects.id = containers.project_id
WHERE containers.id=?
`
	project := ""
	name := ""
	arg1 := []interface{}{id}
	arg2 := []interface{}{&project, &name}
	err := dbQueryRowScan(c.db, q, arg1, arg2)
	if err == sql.ErrNoRows {
		return "", "", ErrNoSuchObject
	}

	return project, name, err
}

// ContainerProjectAndName returns the project and the name of the container
// with the given ID.

ContainerProjectAndName returns the project and the name of the container with the given ID .


func ContainerConfigClear(tx *sql.Tx, id int) error {
	_, err := tx.Exec("DELETE FROM containers_config WHERE container_id=?", id)
	if err != nil {
		return err
	}
	_, err = tx.Exec("DELETE FROM containers_profiles WHERE container_id=?", id)
	if err != nil {
		return err
	}
	_, err = tx.Exec(`DELETE FROM containers_devices_config WHERE id IN
		(SELECT containers_devices_config.id
		 FROM containers_devices_config JOIN containers_devices
		 ON containers_devices_config.container_device_id=containers_devices.id
		 WHERE containers_devices.container_id=?)`, id)
	if err != nil {
		return err
	}
	_, err = tx.Exec("DELETE FROM containers_devices WHERE container_id=?", id)
	return err
}

// ContainerConfigClear removes any config associated with the container with
// the given ID.

ContainerConfigClear removes any config associated with the container with the given ID .


func (c *Cluster) ContainerConfigGet(id int, key string) (string, error) {
	q := "SELECT value FROM containers_config WHERE container_id=? AND key=?"
	value := ""
	arg1 := []interface{}{id, key}
	arg2 := []interface{}{&value}
	err := dbQueryRowScan(c.db, q, arg1, arg2)
	if err == sql.ErrNoRows {
		return "", ErrNoSuchObject
	}

	return value, err
}

func ( c * Cluster ) ContainerConfigGet ( id int , key string ) ( string , error ) { q := " " value := " " arg1 := [ ] interface { } { id , key } arg2 := [ ] interface { } { & value } err := dbQueryRowScan ( c . db , q , arg1 , arg2 ) if err == sql . ErrNoRows { return " " , ErrNoSuchObject } return value , err }

// ContainerConfigGet returns the value of the given key in the configuration
// of the container with the given ID.

ContainerConfigGet returns the value of the given key in the configuration of the container with the given ID .


func (c *Cluster) ContainerConfigRemove(id int, key string) error {
	err := exec(c.db, "DELETE FROM containers_config WHERE key=? AND container_id=?", key, id)
	return err
}

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

// ContainerConfigRemove removes the given key from the config of the container
// with the given ID.

ContainerConfigRemove removes the given key from the config of the container with the given ID .


func (c *Cluster) ContainerSetStateful(id int, stateful bool) error {
	statefulInt := 0
	if stateful {
		statefulInt = 1
	}

	err := exec(c.db, "UPDATE containers SET stateful=? WHERE id=?", statefulInt, id)
	return err
}

func ( c * Cluster ) ContainerSetStateful ( id int , stateful bool ) error { statefulInt := 0 if stateful { statefulInt = 1 } err := exec ( c . db , " " , statefulInt , id ) return err }

// ContainerSetStateful toggles the stateful flag of the container with the
// given ID.

ContainerSetStateful toggles the stateful flag of the container with the given ID .


func ContainerProfilesInsert(tx *sql.Tx, id int, project string, profiles []string) error {
	enabled, err := projectHasProfiles(tx, project)
	if err != nil {
		return errors.Wrap(err, "Check if project has profiles")
	}
	if !enabled {
		project = "default"
	}

	applyOrder := 1
	str := `
INSERT INTO containers_profiles (container_id, profile_id, apply_order)
  VALUES (
    ?,
    (SELECT profiles.id
     FROM profiles
     JOIN projects ON projects.id=profiles.project_id
     WHERE projects.name=? AND profiles.name=?),
    ?
  )
`
	stmt, err := tx.Prepare(str)
	if err != nil {
		return err
	}
	defer stmt.Close()
	for _, profile := range profiles {
		_, err = stmt.Exec(id, project, profile, applyOrder)
		if err != nil {
			logger.Debugf("Error adding profile %s to container: %s",
				profile, err)
			return err
		}
		applyOrder = applyOrder + 1
	}

	return nil
}

// ContainerProfilesInsert associates the container with the given ID with the
// profiles with the given names in the given project.

ContainerProfilesInsert associates the container with the given ID with the profiles with the given names in the given project .


func (c *Cluster) ContainerProfiles(id int) ([]string, error) {
	var name string
	var profiles []string

	query := `
        SELECT name FROM containers_profiles
        JOIN profiles ON containers_profiles.profile_id=profiles.id
		WHERE container_id=?
        ORDER BY containers_profiles.apply_order`
	inargs := []interface{}{id}
	outfmt := []interface{}{name}

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

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

		profiles = append(profiles, name)
	}

	return profiles, nil
}

// ContainerProfiles returns a list of profiles for a given container ID.

ContainerProfiles returns a list of profiles for a given container ID .


func (c *Cluster) ContainerConfig(id int) (map[string]string, error) {
	var key, value string
	q := `SELECT key, value FROM containers_config WHERE container_id=?`

	inargs := []interface{}{id}
	outfmt := []interface{}{key, value}

	// Results is already a slice here, not db Rows anymore.
	results, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return nil, err //SmartError will wrap this and make "not found" errors pretty
	}

	config := map[string]string{}

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

		config[key] = value
	}

	return config, nil
}

// ContainerConfig gets the container configuration map from the DB


func (c *Cluster) ContainerSetState(id int, state string) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		// Set the new value
		str := fmt.Sprintf("INSERT OR REPLACE INTO containers_config (container_id, key, value) VALUES (?, 'volatile.last_state.power', ?)")
		stmt, err := tx.tx.Prepare(str)
		if err != nil {
			return err
		}
		defer stmt.Close()

		if _, err = stmt.Exec(id, state); err != nil {
			return err
		}
		return nil
	})
	return err
}

func
(
c
*
Cluster
)
ContainerSetState
(
id
int
,
state
string
)
error
{
err
:=
c
.
Transaction
(
func
(
tx
*
ClusterTx
)
error
{
// Set the new value
str
:=
fmt
.
Sprintf
(
"
"
)
stmt
,
err
:=
tx
.
tx
.
Prepare
(
str
)
if
err
!=
{
return
err
}
defer
stmt
.
Close
(
)
if
_
,
err
=
stmt
.
Exec
(
id
,
state
)
;
err
!=
{
return
err
}
return
}
)
return
err
}

// ContainerSetState sets the the power state of the container with the given ID.

ContainerSetState sets the the power state of the container with the given ID .


func ContainerUpdate(tx *sql.Tx, id int, description string, architecture int, ephemeral bool,
	expiryDate time.Time) error {
	str := fmt.Sprintf("UPDATE containers SET description=?, architecture=?, ephemeral=?, expiry_date=? WHERE id=?")
	stmt, err := tx.Prepare(str)
	if err != nil {
		return err
	}
	defer stmt.Close()

	ephemeralInt := 0
	if ephemeral {
		ephemeralInt = 1
	}

	if expiryDate.IsZero() {
		_, err = stmt.Exec(description, architecture, ephemeralInt, "", id)
	} else {
		_, err = stmt.Exec(description, architecture, ephemeralInt, expiryDate, id)
	}
	if err != nil {
		return err
	}

	return nil
}

func ContainerUpdate ( tx * sql . Tx , id int , description string , architecture int , ephemeral bool , expiryDate time . Time ) error { str := fmt . Sprintf ( " " ) stmt , err := tx . Prepare ( str ) if err != nil { return err } defer stmt . Close ( ) ephemeralInt := 0 if ephemeral { ephemeralInt = 1 } if expiryDate . IsZero ( ) { _ , err = stmt . Exec ( description , architecture , ephemeralInt , " " , id ) } else { _ , err = stmt . Exec ( description , architecture , ephemeralInt , expiryDate , id ) } if err != nil { return err } return nil }

// ContainerUpdate updates the description, architecture and ephemeral flag of
// the container with the given ID.

ContainerUpdate updates the description architecture and ephemeral flag of the container with the given ID .


func (c *Cluster) ContainerLastUsedUpdate(id int, date time.Time) error {
	stmt := `UPDATE containers SET last_use_date=? WHERE id=?`
	err := exec(c.db, stmt, date, id)
	return err
}

// ContainerLastUsedUpdate updates the last_use_date field of the container
// with the given ID.

ContainerLastUsedUpdate updates the last_use_date field of the container with the given ID .


func (c *Cluster) ContainerGetSnapshots(project, name string) ([]string, error) {
	result := []string{}

	regexp := name + shared.SnapshotDelimiter
	length := len(regexp)
	q := `
SELECT containers.name
  FROM containers
  JOIN projects ON projects.id = containers.project_id
WHERE projects.name=? AND containers.type=? AND SUBSTR(containers.name,1,?)=?
`
	inargs := []interface{}{project, CTypeSnapshot, length, regexp}
	outfmt := []interface{}{name}
	dbResults, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return result, err
	}

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

	return result, nil
}

// ContainerGetSnapshots returns the names of all snapshots of the container
// in the given project with the given name.

ContainerGetSnapshots returns the names of all snapshots of the container in the given project with the given name .


func (c *ClusterTx) ContainerGetSnapshotsFull(project string, name string) ([]Container, error) {
	filter := ContainerFilter{
		Parent:  name,
		Project: project,
		Type:    int(CTypeSnapshot),
	}

	return c.ContainerList(filter)
}

func ( c * ClusterTx ) ContainerGetSnapshotsFull ( project string , name string ) ( [ ] Container , error ) { filter := ContainerFilter { Parent : name , Project : project , Type : int ( CTypeSnapshot ) , } return c . ContainerList ( filter ) }

// ContainerGetSnapshotsFull returns all container objects for snapshots of a given container


func (c *Cluster) ContainerNextSnapshot(project string, name string, pattern string) int {
	base := name + shared.SnapshotDelimiter
	length := len(base)
	q := `
SELECT containers.name
  FROM containers
  JOIN projects ON projects.id = containers.project_id
 WHERE projects.name=? AND containers.type=? AND SUBSTR(containers.name,1,?)=?`
	var numstr string
	inargs := []interface{}{project, CTypeSnapshot, length, base}
	outfmt := []interface{}{numstr}
	results, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return 0
	}
	max := 0

	for _, r := range results {
		snapOnlyName := strings.SplitN(r[0].(string), shared.SnapshotDelimiter, 2)[1]
		fields := strings.SplitN(pattern, "%d", 2)

		var num int
		count, err := fmt.Sscanf(snapOnlyName, fmt.Sprintf("%s%%d%s", fields[0], fields[1]), &num)
		if err != nil || count != 1 {
			continue
		}
		if num >= max {
			max = num + 1
		}
	}

	return max
}

// ContainerNextSnapshot returns the index the next snapshot of the container
// with the given name and pattern should have.

ContainerNextSnapshot returns the index the next snapshot of the container with the given name and pattern should have .


func (c *ClusterTx) ContainerPool(project, containerName string) (string, error) {
	// Get container storage volume. Since container names are globally
	// unique, and their storage volumes carry the same name, their storage
	// volumes are unique too.
	poolName := ""
	query := `
SELECT storage_pools.name FROM storage_pools
  JOIN storage_volumes ON storage_pools.id=storage_volumes.storage_pool_id
  JOIN containers ON containers.name=storage_volumes.name
  JOIN projects ON projects.id=containers.project_id
 WHERE projects.name=? AND storage_volumes.node_id=? AND storage_volumes.name=? AND storage_volumes.type=?
`
	inargs := []interface{}{project, c.nodeID, containerName, StoragePoolVolumeTypeContainer}
	outargs := []interface{}{&poolName}

	err := c.tx.QueryRow(query, inargs...).Scan(outargs...)
	if err != nil {
		if err == sql.ErrNoRows {
			return "", ErrNoSuchObject
		}

		return "", err
	}

	return poolName, nil
}

// ContainerPool returns the storage pool of a given container.

ContainerPool returns the storage pool of a given container .


func (c *Cluster) ContainerGetBackup(project, name string) (ContainerBackupArgs, error) {
	args := ContainerBackupArgs{}
	args.Name = name

	containerOnlyInt := -1
	optimizedStorageInt := -1
	q := `
SELECT containers_backups.id, containers_backups.container_id,
       containers_backups.creation_date, containers_backups.expiry_date,
       containers_backups.container_only, containers_backups.optimized_storage
    FROM containers_backups
    JOIN containers ON containers.id=containers_backups.container_id
    JOIN projects ON projects.id=containers.project_id
    WHERE projects.name=? AND containers_backups.name=?
`
	arg1 := []interface{}{project, name}
	arg2 := []interface{}{&args.ID, &args.ContainerID, &args.CreationDate,
		&args.ExpiryDate, &containerOnlyInt, &optimizedStorageInt}
	err := dbQueryRowScan(c.db, q, arg1, arg2)
	if err != nil {
		if err == sql.ErrNoRows {
			return args, ErrNoSuchObject
		}

		return args, err
	}

	if containerOnlyInt == 1 {
		args.ContainerOnly = true
	}

	if optimizedStorageInt == 1 {
		args.OptimizedStorage = true
	}

	return args, nil
}

// ContainerGetBackup returns the backup with the given name.

ContainerGetBackup returns the backup with the given name .


func (c *Cluster) ContainerGetBackups(project, name string) ([]string, error) {
	var result []string

	q := `SELECT containers_backups.name FROM containers_backups
JOIN containers ON containers_backups.container_id=containers.id
JOIN projects ON projects.id=containers.project_id
WHERE projects.name=? AND containers.name=?`
	inargs := []interface{}{project, name}
	outfmt := []interface{}{name}
	dbResults, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return nil, err
	}

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

	return result, nil
}

// ContainerGetBackups returns the names of all backups of the container
// with the given name.

ContainerGetBackups returns the names of all backups of the container with the given name .


func (c *Cluster) ContainerBackupCreate(args ContainerBackupArgs) error {
	_, err := c.ContainerBackupID(args.Name)
	if err == nil {
		return ErrAlreadyDefined
	}

	err = c.Transaction(func(tx *ClusterTx) error {
		containerOnlyInt := 0
		if args.ContainerOnly {
			containerOnlyInt = 1
		}

		optimizedStorageInt := 0
		if args.OptimizedStorage {
			optimizedStorageInt = 1
		}

		str := fmt.Sprintf("INSERT INTO containers_backups (container_id, name, creation_date, expiry_date, container_only, optimized_storage) VALUES (?, ?, ?, ?, ?, ?)")
		stmt, err := tx.tx.Prepare(str)
		if err != nil {
			return err
		}
		defer stmt.Close()
		result, err := stmt.Exec(args.ContainerID, args.Name,
			args.CreationDate.Unix(), args.ExpiryDate.Unix(), containerOnlyInt,
			optimizedStorageInt)
		if err != nil {
			return err
		}

		_, err = result.LastInsertId()
		if err != nil {
			return fmt.Errorf("Error inserting %s into database", args.Name)
		}

		return nil
	})

	return err
}

func ( c * Cluster ) ContainerBackupCreate ( args ContainerBackupArgs ) error { _ , err := c . ContainerBackupID ( args . Name ) if err == nil { return ErrAlreadyDefined } err = c . Transaction ( func ( tx * ClusterTx ) error { containerOnlyInt := 0 if args . ContainerOnly { containerOnlyInt = 1 } optimizedStorageInt := 0 if args . OptimizedStorage { optimizedStorageInt = 1 } str := fmt . Sprintf ( " " ) stmt , err := tx . tx . Prepare ( str ) if err != nil { return err } defer stmt . Close ( ) result , err := stmt . Exec ( args . ContainerID , args . Name , args . CreationDate . Unix ( ) , args . ExpiryDate . Unix ( ) , containerOnlyInt , optimizedStorageInt ) if err != nil { return err } _ , err = result . LastInsertId ( ) if err != nil { return fmt . Errorf ( " " , args . Name ) } return nil } ) return err }

// ContainerBackupCreate creates a new backup


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

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

	return nil
}

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

// ContainerBackupRemove removes the container backup with the given name from
// the database.

ContainerBackupRemove removes the container backup with the given name from the database .


func (c *Cluster) ContainerBackupRename(oldName, newName string) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		str := fmt.Sprintf("UPDATE containers_backups SET name = ? WHERE name = ?")
		stmt, err := tx.tx.Prepare(str)
		if err != nil {
			return err
		}
		defer stmt.Close()

		logger.Debug(
			"Calling SQL Query",
			log.Ctx{
				"query":   "UPDATE containers_backups SET name = ? WHERE name = ?",
				"oldName": oldName,
				"newName": newName})
		if _, err := stmt.Exec(newName, oldName); err != nil {
			return err
		}

		return nil
	})
	return err
}

func ( c * Cluster ) ContainerBackupRename ( oldName , newName string ) error { err := c . Transaction ( func ( tx * ClusterTx ) error { str := fmt . Sprintf ( " " ) stmt , err := tx . tx . Prepare ( str ) if err != nil { return err } defer stmt . Close ( ) logger . Debug ( " " , log . Ctx { " " : " " , " " : oldName , " " : newName } ) if _ , err := stmt . Exec ( newName , oldName ) ; err != nil { return err } return nil } ) return err }

// ContainerBackupRename renames a container backup from the given current name
// to the new one.

ContainerBackupRename renames a container backup from the given current name to the new one .


func (c *Cluster) ContainerBackupsGetExpired() ([]string, error) {
	var result []string
	var name string
	var expiryDate string

	q := `SELECT containers_backups.name, containers_backups.expiry_date FROM containers_backups`
	outfmt := []interface{}{name, expiryDate}
	dbResults, err := queryScan(c.db, q, nil, outfmt)
	if err != nil {
		return nil, err
	}

	for _, r := range dbResults {
		timestamp := r[1]

		var backupExpiry time.Time
		err = backupExpiry.UnmarshalText([]byte(timestamp.(string)))
		if err != nil {
			return []string{}, err
		}

		if backupExpiry.IsZero() {
			// Backup doesn't expire
			continue
		}

		// Backup has expired
		if time.Now().Unix()-backupExpiry.Unix() >= 0 {
			result = append(result, r[0].(string))
		}
	}

	return result, nil
}

// ContainerBackupsGetExpired returns a list of expired container backups.

ContainerBackupsGetExpired returns a list of expired container backups .


func DefaultOS() *OS {
	newOS := &OS{
		VarDir:   shared.VarPath(),
		CacheDir: shared.CachePath(),
		LogDir:   shared.LogPath(),
	}
	newOS.InotifyWatch.Fd = -1
	newOS.InotifyWatch.Targets = make(map[string]*InotifyTargetInfo)
	return newOS
}

func DefaultOS ( ) * OS { newOS := & OS { VarDir : shared . VarPath ( ) , CacheDir : shared . CachePath ( ) , LogDir : shared . LogPath ( ) , } newOS . InotifyWatch . Fd = - 1 newOS . InotifyWatch . Targets = make ( map [ string ] * InotifyTargetInfo ) return newOS }

// DefaultOS returns a fresh uninitialized OS instance with default values.

DefaultOS returns a fresh uninitialized OS instance with default values .


func (s *OS) Init() error {
	err := s.initDirs()
	if err != nil {
		return err
	}

	s.Architectures, err = util.GetArchitectures()
	if err != nil {
		return err
	}

	s.LxcPath = filepath.Join(s.VarDir, "containers")

	s.BackingFS, err = util.FilesystemDetect(s.LxcPath)
	if err != nil {
		logger.Error("Error detecting backing fs", log.Ctx{"err": err})
	}

	s.IdmapSet = util.GetIdmapSet()
	s.ExecPath = util.GetExecPath()
	s.RunningInUserNS = shared.RunningInUserNS()

	s.initAppArmor()
	s.initCGroup()

	return nil
}

func ( s * OS ) Init ( ) error { err := s . initDirs ( ) if err != nil { return err } s . Architectures , err = util . GetArchitectures ( ) if err != nil { return err } s . LxcPath = filepath . Join ( s . VarDir , " " ) s . BackingFS , err = util . FilesystemDetect ( s . LxcPath ) if err != nil { logger . Error ( " " , log . Ctx { " " : err } ) } s . IdmapSet = util . GetIdmapSet ( ) s . ExecPath = util . GetExecPath ( ) s . RunningInUserNS = shared . RunningInUserNS ( ) s . initAppArmor ( ) s . initCGroup ( ) return nil }


func (op *operation) GetWebsocket(secret string) (*websocket.Conn, error) {
	return op.r.GetOperationWebsocket(op.ID, secret)
}

func ( op * operation ) GetWebsocket ( secret string ) ( * websocket . Conn , error ) { return op . r . GetOperationWebsocket ( op . ID , secret ) }

// GetWebsocket returns a raw websocket connection from the operation


func (op *operation) Refresh() error {
	// Get the current version of the operation
	newOp, _, err := op.r.GetOperation(op.ID)
	if err != nil {
		return err
	}

	// Update the operation struct
	op.Operation = *newOp

	return nil
}

// Refresh pulls the current version of the operation and updates the struct


func (op *remoteOperation) CancelTarget() error {
	if op.targetOp == nil {
		return fmt.Errorf("No associated target operation")
	}

	return op.targetOp.Cancel()
}

func ( op * remoteOperation ) CancelTarget ( ) error { if op . targetOp == nil { return fmt . Errorf ( " " ) } return op . targetOp . Cancel ( ) }

// CancelTarget attempts to cancel the target operation


func (op *remoteOperation) GetTarget() (*api.Operation, error) {
	if op.targetOp == nil {
		return nil, fmt.Errorf("No associated target operation")
	}

	opAPI := op.targetOp.Get()
	return &opAPI, nil
}

func ( op * remoteOperation ) GetTarget ( ) ( * api . Operation , error ) { if op . targetOp == nil { return nil , fmt . Errorf ( " " ) } opAPI := op . targetOp . Get ( ) return & opAPI , nil }

// GetTarget returns the target operation


func (e *Endpoints) up(config *Config) error {
	e.mu.Lock()
	defer e.mu.Unlock()

	e.servers = map[kind]*http.Server{
		devlxd:  config.DevLxdServer,
		local:   config.RestServer,
		network: config.RestServer,
		cluster: config.RestServer,
		pprof:   pprofCreateServer(),
	}
	e.cert = config.Cert
	e.inherited = map[kind]bool{}

	var err error

	// Check for socket activation.
	systemdListeners := util.GetListeners(e.systemdListenFDsStart)
	if len(systemdListeners) > 0 {
		e.listeners = activatedListeners(systemdListeners, e.cert)
		for kind := range e.listeners {
			e.inherited[kind] = true
		}
	} else {
		e.listeners = map[kind]net.Listener{}

		e.listeners[local], err = localCreateListener(config.UnixSocket, config.LocalUnixSocketGroup)
		if err != nil {
			return fmt.Errorf("local endpoint: %v", err)
		}
	}

	// Start the devlxd listener
	e.listeners[devlxd], err = createDevLxdlListener(config.Dir)
	if err != nil {
		return err
	}

	if config.NetworkAddress != "" {
		listener, ok := e.listeners[network]
		if ok {
			logger.Infof("Replacing inherited TCP socket with configured one")
			listener.Close()
			e.inherited[network] = false
		}

		// Errors here are not fatal and are just logged.
		e.listeners[network] = networkCreateListener(config.NetworkAddress, e.cert)

		isCovered := util.IsAddressCovered(config.ClusterAddress, config.NetworkAddress)
		if config.ClusterAddress != "" && !isCovered {
			e.listeners[cluster], err = clusterCreateListener(config.ClusterAddress, e.cert)
			if err != nil {
				return err
			}

			logger.Infof("Starting cluster handler:")
			e.serveHTTP(cluster)
		}

	}

	if config.DebugAddress != "" {
		e.listeners[pprof], err = pprofCreateListener(config.DebugAddress)
		if err != nil {
			return err
		}

		logger.Infof("Starting pprof handler:")
		e.serveHTTP(pprof)
	}

	logger.Infof("Starting /dev/lxd handler:")
	e.serveHTTP(devlxd)

	logger.Infof("REST API daemon:")
	e.serveHTTP(local)
	e.serveHTTP(network)

	return nil
}

// Up brings up all configured endpoints and starts accepting HTTP requests.

Up brings up all configured endpoints and starts accepting HTTP requests .


func (e *Endpoints) Down() error {
	e.mu.Lock()
	defer e.mu.Unlock()

	if e.listeners[network] != nil || e.listeners[local] != nil {
		logger.Infof("Stopping REST API handler:")
		err := e.closeListener(network)
		if err != nil {
			return err
		}

		err = e.closeListener(local)
		if err != nil {
			return err
		}
	}

	if e.listeners[cluster] != nil {
		logger.Infof("Stopping cluster handler:")
		err := e.closeListener(cluster)
		if err != nil {
			return err
		}
	}

	if e.listeners[devlxd] != nil {
		logger.Infof("Stopping /dev/lxd handler:")
		err := e.closeListener(devlxd)
		if err != nil {
			return err
		}
	}

	if e.listeners[pprof] != nil {
		logger.Infof("Stopping pprof handler:")
		err := e.closeListener(pprof)
		if err != nil {
			return err
		}
	}

	if e.tomb != nil {
		e.tomb.Kill(nil)
		e.tomb.Wait()
	}

	return nil
}

func ( e * Endpoints ) Down ( ) error { e . mu . Lock ( ) defer e . mu . Unlock ( ) if e . listeners [ network ] != nil || e . listeners [ local ] != nil { logger . Infof ( " " ) err := e . closeListener ( network ) if err != nil { return err } err = e . closeListener ( local ) if err != nil { return err } } if e . listeners [ cluster ] != nil { logger . Infof ( " " ) err := e . closeListener ( cluster ) if err != nil { return err } } if e . listeners [ devlxd ] != nil { logger . Infof ( " " ) err := e . closeListener ( devlxd ) if err != nil { return err } } if e . listeners [ pprof ] != nil { logger . Infof ( " " ) err := e . closeListener ( pprof ) if err != nil { return err } } if e . tomb != nil { e . tomb . Kill ( nil ) e . tomb . Wait ( ) } return nil }

// Down brings down all endpoints and stops serving HTTP requests.

Down brings down all endpoints and stops serving HTTP requests .


func (e *Endpoints) serveHTTP(kind kind) {
	listener := e.listeners[kind]

	if listener == nil {
		return
	}

	ctx := log.Ctx{"socket": listener.Addr()}
	if e.inherited[kind] {
		ctx["inherited"] = true
	}

	message := fmt.Sprintf(" - binding %s", descriptions[kind])
	logger.Info(message, ctx)

	server := e.servers[kind]

	// Defer the creation of the tomb, so Down() doesn't wait on it unless
	// we actually have spawned at least a server.
	if e.tomb == nil {
		e.tomb = &tomb.Tomb{}
	}

	e.tomb.Go(func() error {
		server.Serve(listener)
		return nil
	})
}

// Start an HTTP server for the endpoint associated with the given code.

Start an HTTP server for the endpoint associated with the given code .


func (e *Endpoints) closeListener(kind kind) error {
	listener := e.listeners[kind]
	if listener == nil {
		return nil
	}
	delete(e.listeners, kind)

	logger.Info(" - closing socket", log.Ctx{"socket": listener.Addr()})

	return listener.Close()
}

func ( e * Endpoints ) closeListener ( kind kind ) error { listener := e . listeners [ kind ] if listener == nil { return nil } delete ( e . listeners , kind ) logger . Info ( " " , log . Ctx { " " : listener . Addr ( ) } ) return listener . Close ( ) }

// Stop the HTTP server of the endpoint associated with the given code. The
// associated socket will be shutdown too.

Stop the HTTP server of the endpoint associated with the given code . The associated socket will be shutdown too .


func activatedListeners(systemdListeners []net.Listener, cert *shared.CertInfo) map[kind]net.Listener {
	listeners := map[kind]net.Listener{}
	for _, listener := range systemdListeners {
		var kind kind
		switch listener.(type) {
		case *net.UnixListener:
			kind = local
		case *net.TCPListener:
			kind = network
			listener = networkTLSListener(listener, cert)
		default:
			continue
		}
		listeners[kind] = listener
	}
	return listeners
}

func activatedListeners ( systemdListeners [ ] net . Listener , cert * shared . CertInfo ) map [ kind ] net . Listener { listeners := map [ kind ] net . Listener { } for _ , listener := range systemdListeners { var kind kind switch listener . ( type ) { case * net . UnixListener : kind = local case * net . TCPListener : kind = network listener = networkTLSListener ( listener , cert ) default : continue } listeners [ kind ] = listener } return listeners }

// Use the listeners associated with the file descriptors passed via
// socket-based activation.

Use the listeners associated with the file descriptors passed via socket - based activation .


func (c *Config) CandidServer() (string, string, int64, string) {
	return c.m.GetString("candid.api.url"),
		c.m.GetString("candid.api.key"),
		c.m.GetInt64("candid.expiry"),
		c.m.GetString("candid.domains")
}

func ( c * Config ) CandidServer ( ) ( string , string , int64 , string ) { return c . m . GetString ( " " ) , c . m . GetString ( " " ) , c . m . GetInt64 ( " " ) , c . m . GetString ( " " ) }

// CandidServer returns all the Candid settings needed to connect to a server.

CandidServer returns all the Candid settings needed to connect to a server .


func (c *Config) RBACServer() (string, string, int64, string, string, string, string) {
	return c.m.GetString("rbac.api.url"),
		c.m.GetString("rbac.api.key"),
		c.m.GetInt64("rbac.expiry"),
		c.m.GetString("rbac.agent.url"),
		c.m.GetString("rbac.agent.username"),
		c.m.GetString("rbac.agent.private_key"),
		c.m.GetString("rbac.agent.public_key")
}

func ( c * Config ) RBACServer ( ) ( string , string , int64 , string , string , string , string ) { return c . m . GetString ( " " ) , c . m . GetString ( " " ) , c . m . GetInt64 ( " " ) , c . m . GetString ( " " ) , c . m . GetString ( " " ) , c . m . GetString ( " " ) , c . m . GetString ( " " ) }

// RBACServer returns all the Candid settings needed to connect to a server.

RBACServer returns all the Candid settings needed to connect to a server .


func (c *Config) AutoUpdateInterval() time.Duration {
	n := c.m.GetInt64("images.auto_update_interval")
	return time.Duration(n) * time.Hour
}

func ( c * Config ) AutoUpdateInterval ( ) time . Duration { n := c . m . GetInt64 ( " " ) return time . Duration ( n ) * time . Hour }

// AutoUpdateInterval returns the configured images auto update interval.

AutoUpdateInterval returns the configured images auto update interval .


func (c *Config) MAASController() (string, string) {
	url := c.m.GetString("maas.api.url")
	key := c.m.GetString("maas.api.key")
	return url, key
}

func ( c * Config ) MAASController ( ) ( string , string ) { url := c . m . GetString ( " " ) key := c . m . GetString ( " " ) return url , key }

// MAASController the configured MAAS url and key, if any.

MAASController the configured MAAS url and key if any .


func (c *Config) OfflineThreshold() time.Duration {
	n := c.m.GetInt64("cluster.offline_threshold")
	return time.Duration(n) * time.Second
}

func ( c * Config ) OfflineThreshold ( ) time . Duration { n := c . m . GetInt64 ( " " ) return time . Duration ( n ) * time . Second }

// OfflineThreshold returns the configured heartbeat threshold, i.e. the
// number of seconds before after which an unresponsive node is considered
// offline..

OfflineThreshold returns the configured heartbeat threshold i . e . the number of seconds before after which an unresponsive node is considered offline ..


func ConfigGetString(cluster *db.Cluster, key string) (string, error) {
	config, err := configGet(cluster)
	if err != nil {
		return "", err
	}
	return config.m.GetString(key), nil
}

func ConfigGetString ( cluster * db . Cluster , key string ) ( string , error ) { config , err := configGet ( cluster ) if err != nil { return " " , err } return config . m . GetString ( key ) , nil }

// ConfigGetString is a convenience for loading the cluster configuration and
// returning the value of a particular key.
//
// It's a deprecated API meant to be used by call sites that are not
// interacting with the database in a transactional way.

ConfigGetString is a convenience for loading the cluster configuration and returning the value of a particular key . It s a deprecated API meant to be used by call sites that are not interacting with the database in a transactional way .


func ConfigGetBool(cluster *db.Cluster, key string) (bool, error) {
	config, err := configGet(cluster)
	if err != nil {
		return false, err
	}
	return config.m.GetBool(key), nil
}

func ConfigGetBool ( cluster * db . Cluster , key string ) ( bool , error ) { config , err := configGet ( cluster ) if err != nil { return false , err } return config . m . GetBool ( key ) , nil }

// ConfigGetBool is a convenience for loading the cluster configuration and
// returning the value of a particular boolean key.
//
// It's a deprecated API meant to be used by call sites that are not
// interacting with the database in a transactional way.

ConfigGetBool is a convenience for loading the cluster configuration and returning the value of a particular boolean key . It s a deprecated API meant to be used by call sites that are not interacting with the database in a transactional way .