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

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

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

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

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

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

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