lxd/db/containers.go
func (c *ClusterTx) ContainerNodeMove(oldName, newName, newNode string) error {
	// First check that the container to be moved is backed by a ceph
	// volume.
	poolName, err := c.ContainerPool("default", oldName)
	if err != nil {
		return errors.Wrap(err, "failed to get container's storage pool name")
	}
	poolID, err := c.StoragePoolID(poolName)
	if err != nil {
		return errors.Wrap(err, "failed to get container's storage pool ID")
	}
	poolDriver, err := c.StoragePoolDriver(poolID)
	if err != nil {
		return errors.Wrap(err, "failed to get container's storage pool driver")
	}
	if poolDriver != "ceph" {
		return fmt.Errorf("container's storage pool is not of type ceph")
	}

	// Update the name of the container and of its snapshots, and the node
	// ID they are associated with.
	containerID, err := c.ContainerID("default", oldName)
	if err != nil {
		return errors.Wrap(err, "failed to get container's ID")
	}
	snapshots, err := c.SnapshotIDsAndNames(oldName)
	if err != nil {
		return errors.Wrap(err, "failed to get container's snapshots")
	}
	node, err := c.NodeByName(newNode)
	if err != nil {
		return errors.Wrap(err, "failed to get new node's info")
	}
	stmt := "UPDATE containers SET node_id=?, name=? WHERE id=?"
	result, err := c.tx.Exec(stmt, node.ID, newName, containerID)
	if err != nil {
		return errors.Wrap(err, "failed to update container's name and node ID")
	}
	n, err := result.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "failed to get rows affected by container update")
	}
	if n != 1 {
		return fmt.Errorf("unexpected number of updated rows in containers table: %d", n)
	}
	for snapshotID, snapshotName := range snapshots {
		newSnapshotName := newName + shared.SnapshotDelimiter + snapshotName
		stmt := "UPDATE containers SET node_id=?, name=? WHERE id=?"
		result, err := c.tx.Exec(stmt, node.ID, newSnapshotName, snapshotID)
		if err != nil {
			return errors.Wrap(err, "failed to update snapshot's name and node ID")
		}
		n, err := result.RowsAffected()
		if err != nil {
			return errors.Wrap(err, "failed to get rows affected by snapshot update")
		}
		if n != 1 {
			return fmt.Errorf("unexpected number of updated snapshot rows: %d", n)
		}
	}

	// No need to update storage_volumes if the name is identical
	if newName == oldName {
		return nil
	}

	// Update the container's and snapshots' storage volume name (since this is ceph,
	// there's a clone of the volume for each node).
	count, err := c.NodesCount()
	if err != nil {
		return errors.Wrap(err, "failed to get node's count")
	}
	stmt = "UPDATE storage_volumes SET name=? WHERE name=? AND storage_pool_id=? AND type=?"
	result, err = c.tx.Exec(stmt, newName, oldName, poolID, StoragePoolVolumeTypeContainer)
	if err != nil {
		return errors.Wrap(err, "failed to update container's volume name")
	}
	n, err = result.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "failed to get rows affected by container volume update")
	}
	if n != int64(count) {
		return fmt.Errorf("unexpected number of updated rows in volumes table: %d", n)
	}
	for _, snapshotName := range snapshots {
		oldSnapshotName := oldName + shared.SnapshotDelimiter + snapshotName
		newSnapshotName := newName + shared.SnapshotDelimiter + snapshotName
		stmt := "UPDATE storage_volumes SET name=? WHERE name=? AND storage_pool_id=? AND type=?"
		result, err := c.tx.Exec(
			stmt, newSnapshotName, oldSnapshotName, poolID, StoragePoolVolumeTypeContainer)
		if err != nil {
			return errors.Wrap(err, "failed to update snapshot volume")
		}
		n, err = result.RowsAffected()
		if err != nil {
			return errors.Wrap(err, "failed to get rows affected by snapshot volume update")
		}
		if n != int64(count) {
			return fmt.Errorf("unexpected number of updated snapshots in volumes table: %d", n)
		}
	}

	return nil
}
func (c *ClusterTx) ContainerNodeMove(oldName, newName, newNode string) error {
	// First check that the container to be moved is backed by a ceph
	// volume.
	poolName, err := c.ContainerPool("default", oldName)
	if err != nil {
		return errors.Wrap(err, "failed to get container's storage pool name")
	}
	poolID, err := c.StoragePoolID(poolName)
	if err != nil {
		return errors.Wrap(err, "failed to get container's storage pool ID")
	}
	poolDriver, err := c.StoragePoolDriver(poolID)
	if err != nil {
		return errors.Wrap(err, "failed to get container's storage pool driver")
	}
	if poolDriver != "ceph" {
		return fmt.Errorf("container's storage pool is not of type ceph")
	}

	// Update the name of the container and of its snapshots, and the node
	// ID they are associated with.
	containerID, err := c.ContainerID("default", oldName)
	if err != nil {
		return errors.Wrap(err, "failed to get container's ID")
	}
	snapshots, err := c.SnapshotIDsAndNames(oldName)
	if err != nil {
		return errors.Wrap(err, "failed to get container's snapshots")
	}
	node, err := c.NodeByName(newNode)
	if err != nil {
		return errors.Wrap(err, "failed to get new node's info")
	}
	stmt := "UPDATE containers SET node_id=?, name=? WHERE id=?"
	result, err := c.tx.Exec(stmt, node.ID, newName, containerID)
	if err != nil {
		return errors.Wrap(err, "failed to update container's name and node ID")
	}
	n, err := result.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "failed to get rows affected by container update")
	}
	if n != 1 {
		return fmt.Errorf("unexpected number of updated rows in containers table: %d", n)
	}
	for snapshotID, snapshotName := range snapshots {
		newSnapshotName := newName + shared.SnapshotDelimiter + snapshotName
		stmt := "UPDATE containers SET node_id=?, name=? WHERE id=?"
		result, err := c.tx.Exec(stmt, node.ID, newSnapshotName, snapshotID)
		if err != nil {
			return errors.Wrap(err, "failed to update snapshot's name and node ID")
		}
		n, err := result.RowsAffected()
		if err != nil {
			return errors.Wrap(err, "failed to get rows affected by snapshot update")
		}
		if n != 1 {
			return fmt.Errorf("unexpected number of updated snapshot rows: %d", n)
		}
	}

	// No need to update storage_volumes if the name is identical
	if newName == oldName {
		return nil
	}

	// Update the container's and snapshots' storage volume name (since this is ceph,
	// there's a clone of the volume for each node).
	count, err := c.NodesCount()
	if err != nil {
		return errors.Wrap(err, "failed to get node's count")
	}
	stmt = "UPDATE storage_volumes SET name=? WHERE name=? AND storage_pool_id=? AND type=?"
	result, err = c.tx.Exec(stmt, newName, oldName, poolID, StoragePoolVolumeTypeContainer)
	if err != nil {
		return errors.Wrap(err, "failed to update container's volume name")
	}
	n, err = result.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "failed to get rows affected by container volume update")
	}
	if n != int64(count) {
		return fmt.Errorf("unexpected number of updated rows in volumes table: %d", n)
	}
	for _, snapshotName := range snapshots {
		oldSnapshotName := oldName + shared.SnapshotDelimiter + snapshotName
		newSnapshotName := newName + shared.SnapshotDelimiter + snapshotName
		stmt := "UPDATE storage_volumes SET name=? WHERE name=? AND storage_pool_id=? AND type=?"
		result, err := c.tx.Exec(
			stmt, newSnapshotName, oldSnapshotName, poolID, StoragePoolVolumeTypeContainer)
		if err != nil {
			return errors.Wrap(err, "failed to update snapshot volume")
		}
		n, err = result.RowsAffected()
		if err != nil {
			return errors.Wrap(err, "failed to get rows affected by snapshot volume update")
		}
		if n != int64(count) {
			return fmt.Errorf("unexpected number of updated snapshots in volumes table: %d", n)
		}
	}

	return nil
}
// ContainerNodeMove changes the node associated with a container.
//
// It's meant to be used when moving a non-running container backed by ceph
// from one cluster node to another.