func clusterInitMember(d, client lxd.ContainerServer, memberConfig []api.ClusterMemberConfigKey) error {
	data := initDataNode{}

	// Fetch all pools currently defined in the cluster.
	pools, err := client.GetStoragePools()
	if err != nil {
		return errors.Wrap(err, "Failed to fetch information about cluster storage pools")
	}

	// Merge the returned storage pools configs with the node-specific
	// configs provided by the user.
	for _, pool := range pools {
		// Skip pending pools.
		if pool.Status == "Pending" {
			continue
		}

		// Skip ceph pools since they have no node-specific key and
		// don't need to be defined on joining nodes.
		if pool.Driver == "ceph" {
			continue
		}

		logger.Debugf("Populating init data for storage pool %s", pool.Name)

		post := api.StoragePoolsPost{
			StoragePoolPut: pool.StoragePoolPut,
			Driver:         pool.Driver,
			Name:           pool.Name,
		}

		// Delete config keys that are automatically populated by LXD
		delete(post.Config, "volatile.initial_source")
		delete(post.Config, "zfs.pool_name")

		// Apply the node-specific config supplied by the user.
		for _, config := range memberConfig {
			if config.Entity != "storage-pool" {
				continue
			}

			if config.Name != pool.Name {
				continue
			}

			if !shared.StringInSlice(config.Key, db.StoragePoolNodeConfigKeys) {
				logger.Warnf("Ignoring config key %s for storage pool %s", config.Key, config.Name)
				continue
			}

			post.Config[config.Key] = config.Value
		}

		data.StoragePools = append(data.StoragePools, post)
	}

	// Fetch all networks currently defined in the cluster.
	networks, err := client.GetNetworks()
	if err != nil {
		return errors.Wrap(err, "Failed to fetch information about cluster networks")
	}

	// Merge the returned storage networks configs with the node-specific
	// configs provided by the user.
	for _, network := range networks {
		// Skip not-managed or pending networks
		if !network.Managed || network.Status == "Pending" {
			continue
		}

		post := api.NetworksPost{
			NetworkPut: network.NetworkPut,
			Managed:    true,
			Name:       network.Name,
			Type:       network.Type,
		}

		// Apply the node-specific config supplied by the user.
		for _, config := range memberConfig {
			if config.Entity != "network" {
				continue
			}

			if config.Name != network.Name {
				continue
			}

			if !shared.StringInSlice(config.Key, db.NetworkNodeConfigKeys) {
				logger.Warnf("Ignoring config key %s for network %s", config.Key, config.Name)
				continue
			}

			post.Config[config.Key] = config.Value
		}

		data.Networks = append(data.Networks, post)
	}

	revert, err := initDataNodeApply(d, data)
	if err != nil {
		revert()
		return errors.Wrap(err, "Failed to initialize storage pools and networks")
	}

	return nil
}
func clusterInitMember(d, client lxd.ContainerServer, memberConfig []api.ClusterMemberConfigKey) error {
	data := initDataNode{}

	// Fetch all pools currently defined in the cluster.
	pools, err := client.GetStoragePools()
	if err != nil {
		return errors.Wrap(err, "Failed to fetch information about cluster storage pools")
	}

	// Merge the returned storage pools configs with the node-specific
	// configs provided by the user.
	for _, pool := range pools {
		// Skip pending pools.
		if pool.Status == "Pending" {
			continue
		}

		// Skip ceph pools since they have no node-specific key and
		// don't need to be defined on joining nodes.
		if pool.Driver == "ceph" {
			continue
		}

		logger.Debugf("Populating init data for storage pool %s", pool.Name)

		post := api.StoragePoolsPost{
			StoragePoolPut: pool.StoragePoolPut,
			Driver:         pool.Driver,
			Name:           pool.Name,
		}

		// Delete config keys that are automatically populated by LXD
		delete(post.Config, "volatile.initial_source")
		delete(post.Config, "zfs.pool_name")

		// Apply the node-specific config supplied by the user.
		for _, config := range memberConfig {
			if config.Entity != "storage-pool" {
				continue
			}

			if config.Name != pool.Name {
				continue
			}

			if !shared.StringInSlice(config.Key, db.StoragePoolNodeConfigKeys) {
				logger.Warnf("Ignoring config key %s for storage pool %s", config.Key, config.Name)
				continue
			}

			post.Config[config.Key] = config.Value
		}

		data.StoragePools = append(data.StoragePools, post)
	}

	// Fetch all networks currently defined in the cluster.
	networks, err := client.GetNetworks()
	if err != nil {
		return errors.Wrap(err, "Failed to fetch information about cluster networks")
	}

	// Merge the returned storage networks configs with the node-specific
	// configs provided by the user.
	for _, network := range networks {
		// Skip not-managed or pending networks
		if !network.Managed || network.Status == "Pending" {
			continue
		}

		post := api.NetworksPost{
			NetworkPut: network.NetworkPut,
			Managed:    true,
			Name:       network.Name,
			Type:       network.Type,
		}

		// Apply the node-specific config supplied by the user.
		for _, config := range memberConfig {
			if config.Entity != "network" {
				continue
			}

			if config.Name != network.Name {
				continue
			}

			if !shared.StringInSlice(config.Key, db.NetworkNodeConfigKeys) {
				logger.Warnf("Ignoring config key %s for network %s", config.Key, config.Name)
				continue
			}

			post.Config[config.Key] = config.Value
		}

		data.Networks = append(data.Networks, post)
	}

	revert, err := initDataNodeApply(d, data)
	if err != nil {
		revert()
		return errors.Wrap(err, "Failed to initialize storage pools and networks")
	}

	return nil
}
// Initialize storage pools and networks on this node.
//
// We pass to LXD client instances, one connected to ourselves (the joining
// node) and one connected to the target cluster node to join.