lxd/container_post.go
func containerPostClusteringMigrateWithCeph(d *Daemon, c container, project, oldName, newName, newNode string) Response {
	run := func(*operation) error {
		// If source node is online (i.e. we're serving the request on
		// it, and c != nil), let's unmap the RBD volume locally
		if c != nil {
			logger.Debugf(`Renaming RBD storage volume for source container "%s" from "%s" to "%s"`, c.Name(), c.Name(), newName)
			poolName, err := c.StoragePool()
			if err != nil {
				return errors.Wrap(err, "Failed to get source container's storage pool name")
			}
			_, pool, err := d.cluster.StoragePoolGet(poolName)
			if err != nil {
				return errors.Wrap(err, "Failed to get source container's storage pool")
			}
			if pool.Driver != "ceph" {
				return fmt.Errorf("Source container's storage pool is not of type ceph")
			}
			si, err := storagePoolVolumeContainerLoadInit(d.State(), c.Project(), c.Name())
			if err != nil {
				return errors.Wrap(err, "Failed to initialize source container's storage pool")
			}
			s, ok := si.(*storageCeph)
			if !ok {
				return fmt.Errorf("Unexpected source container storage backend")
			}
			err = cephRBDVolumeUnmap(s.ClusterName, s.OSDPoolName, c.Name(),
				storagePoolVolumeTypeNameContainer, s.UserName, true)
			if err != nil {
				return errors.Wrap(err, "Failed to unmap source container's RBD volume")
			}

		}

		// Re-link the database entries against the new node name.
		var poolName string
		err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
			err := tx.ContainerNodeMove(oldName, newName, newNode)
			if err != nil {
				return err
			}
			poolName, err = tx.ContainerPool(project, newName)
			if err != nil {
				return err
			}
			return nil
		})
		if err != nil {
			return errors.Wrap(err, "Failed to relink container database data")
		}

		// Rename the RBD volume if necessary.
		if newName != oldName {
			s := storageCeph{}
			_, s.pool, err = d.cluster.StoragePoolGet(poolName)
			if err != nil {
				return errors.Wrap(err, "Failed to get storage pool")
			}
			if err != nil {
				return errors.Wrap(err, "Failed to get storage pool")
			}
			err = s.StoragePoolInit()
			if err != nil {
				return errors.Wrap(err, "Failed to initialize ceph storage pool")
			}
			err = cephRBDVolumeRename(s.ClusterName, s.OSDPoolName,
				storagePoolVolumeTypeNameContainer, oldName, newName, s.UserName)
			if err != nil {
				return errors.Wrap(err, "Failed to rename ceph RBD volume")
			}
		}

		// Create the container mount point on the target node
		cert := d.endpoints.NetworkCert()
		client, err := cluster.ConnectIfContainerIsRemote(d.cluster, project, newName, cert)
		if err != nil {
			return errors.Wrap(err, "Failed to connect to target node")
		}
		if client == nil {
			err := containerPostCreateContainerMountPoint(d, project, newName)
			if err != nil {
				return errors.Wrap(err, "Failed to create mount point on target node")
			}
		} else {
			path := fmt.Sprintf("/internal/cluster/container-moved/%s", newName)
			resp, _, err := client.RawQuery("POST", path, nil, "")
			if err != nil {
				return errors.Wrap(err, "Failed to create mount point on target node")
			}
			if resp.StatusCode != 200 {
				return fmt.Errorf("Failed to create mount point on target node: %s", resp.Error)
			}
		}

		return nil
	}

	resources := map[string][]string{}
	resources["containers"] = []string{oldName}
	op, err := operationCreate(d.cluster, project, operationClassTask, db.OperationContainerMigrate, resources, nil, run, nil, nil)
	if err != nil {
		return InternalError(err)
	}

	return OperationResponse(op)
}
func containerPostClusteringMigrateWithCeph(d *Daemon, c container, project, oldName, newName, newNode string) Response {
	run := func(*operation) error {
		// If source node is online (i.e. we're serving the request on
		// it, and c != nil), let's unmap the RBD volume locally
		if c != nil {
			logger.Debugf(`Renaming RBD storage volume for source container "%s" from "%s" to "%s"`, c.Name(), c.Name(), newName)
			poolName, err := c.StoragePool()
			if err != nil {
				return errors.Wrap(err, "Failed to get source container's storage pool name")
			}
			_, pool, err := d.cluster.StoragePoolGet(poolName)
			if err != nil {
				return errors.Wrap(err, "Failed to get source container's storage pool")
			}
			if pool.Driver != "ceph" {
				return fmt.Errorf("Source container's storage pool is not of type ceph")
			}
			si, err := storagePoolVolumeContainerLoadInit(d.State(), c.Project(), c.Name())
			if err != nil {
				return errors.Wrap(err, "Failed to initialize source container's storage pool")
			}
			s, ok := si.(*storageCeph)
			if !ok {
				return fmt.Errorf("Unexpected source container storage backend")
			}
			err = cephRBDVolumeUnmap(s.ClusterName, s.OSDPoolName, c.Name(),
				storagePoolVolumeTypeNameContainer, s.UserName, true)
			if err != nil {
				return errors.Wrap(err, "Failed to unmap source container's RBD volume")
			}

		}

		// Re-link the database entries against the new node name.
		var poolName string
		err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
			err := tx.ContainerNodeMove(oldName, newName, newNode)
			if err != nil {
				return err
			}
			poolName, err = tx.ContainerPool(project, newName)
			if err != nil {
				return err
			}
			return nil
		})
		if err != nil {
			return errors.Wrap(err, "Failed to relink container database data")
		}

		// Rename the RBD volume if necessary.
		if newName != oldName {
			s := storageCeph{}
			_, s.pool, err = d.cluster.StoragePoolGet(poolName)
			if err != nil {
				return errors.Wrap(err, "Failed to get storage pool")
			}
			if err != nil {
				return errors.Wrap(err, "Failed to get storage pool")
			}
			err = s.StoragePoolInit()
			if err != nil {
				return errors.Wrap(err, "Failed to initialize ceph storage pool")
			}
			err = cephRBDVolumeRename(s.ClusterName, s.OSDPoolName,
				storagePoolVolumeTypeNameContainer, oldName, newName, s.UserName)
			if err != nil {
				return errors.Wrap(err, "Failed to rename ceph RBD volume")
			}
		}

		// Create the container mount point on the target node
		cert := d.endpoints.NetworkCert()
		client, err := cluster.ConnectIfContainerIsRemote(d.cluster, project, newName, cert)
		if err != nil {
			return errors.Wrap(err, "Failed to connect to target node")
		}
		if client == nil {
			err := containerPostCreateContainerMountPoint(d, project, newName)
			if err != nil {
				return errors.Wrap(err, "Failed to create mount point on target node")
			}
		} else {
			path := fmt.Sprintf("/internal/cluster/container-moved/%s", newName)
			resp, _, err := client.RawQuery("POST", path, nil, "")
			if err != nil {
				return errors.Wrap(err, "Failed to create mount point on target node")
			}
			if resp.StatusCode != 200 {
				return fmt.Errorf("Failed to create mount point on target node: %s", resp.Error)
			}
		}

		return nil
	}

	resources := map[string][]string{}
	resources["containers"] = []string{oldName}
	op, err := operationCreate(d.cluster, project, operationClassTask, db.OperationContainerMigrate, resources, nil, run, nil, nil)
	if err != nil {
		return InternalError(err)
	}

	return OperationResponse(op)
}
// Special case migrating a container backed by ceph across two cluster nodes.