lxd/container_post.go
func containerPostClusteringMigrate(d *Daemon, c container, oldName, newName, newNode string) Response {
	cert := d.endpoints.NetworkCert()

	var sourceAddress string
	var targetAddress string

	// Save the original value of the "volatile.apply_template" config key,
	// since we'll want to preserve it in the copied container.
	origVolatileApplyTemplate := c.LocalConfig()["volatile.apply_template"]

	err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
		var err error

		sourceAddress, err = tx.NodeAddress()
		if err != nil {
			return errors.Wrap(err, "Failed to get local node address")
		}

		node, err := tx.NodeByName(newNode)
		if err != nil {
			return errors.Wrap(err, "Failed to get new node address")
		}
		targetAddress = node.Address

		return nil
	})
	if err != nil {
		return SmartError(err)
	}

	run := func(*operation) error {
		// Connect to the source host, i.e. ourselves (the node the container is running on).
		source, err := cluster.Connect(sourceAddress, cert, false)
		if err != nil {
			return errors.Wrap(err, "Failed to connect to source server")
		}

		// Connect to the destination host, i.e. the node to migrate the container to.
		dest, err := cluster.Connect(targetAddress, cert, false)
		if err != nil {
			return errors.Wrap(err, "Failed to connect to destination server")
		}
		dest = dest.UseTarget(newNode)

		destName := newName
		isSameName := false

		// If no new name was provided, the user wants to keep the same
		// container name. In that case we need to generate a temporary
		// name.
		if destName == "" || destName == oldName {
			isSameName = true
			destName = fmt.Sprintf("move-%s", uuid.NewRandom().String())
		}

		// First make a copy on the new node of the container to be moved.
		entry, _, err := source.GetContainer(oldName)
		if err != nil {
			return errors.Wrap(err, "Failed to get container info")
		}

		args := lxd.ContainerCopyArgs{
			Name: destName,
			Mode: "pull",
		}

		copyOp, err := dest.CopyContainer(source, *entry, &args)
		if err != nil {
			return errors.Wrap(err, "Failed to issue copy container API request")
		}

		err = copyOp.Wait()
		if err != nil {
			return errors.Wrap(err, "Copy container operation failed")
		}

		// Delete the container on the original node.
		deleteOp, err := source.DeleteContainer(oldName)
		if err != nil {
			return errors.Wrap(err, "Failed to issue delete container API request")
		}

		err = deleteOp.Wait()
		if err != nil {
			return errors.Wrap(err, "Delete container operation failed")
		}

		// If the destination name is not set, we have generated a random name for
		// the new container, so we need to rename it.
		if isSameName {
			containerPost := api.ContainerPost{
				Name: oldName,
			}

			op, err := dest.RenameContainer(destName, containerPost)
			if err != nil {
				return errors.Wrap(err, "Failed to issue rename container API request")
			}

			err = op.Wait()
			if err != nil {
				return errors.Wrap(err, "Rename container operation failed")
			}
			destName = oldName
		}

		// Restore the original value of "volatile.apply_template"
		id, err := d.cluster.ContainerID(destName)
		if err != nil {
			return errors.Wrap(err, "Failed to get ID of moved container")
		}

		err = d.cluster.ContainerConfigRemove(id, "volatile.apply_template")
		if err != nil {
			return errors.Wrap(err, "Failed to remove volatile.apply_template config key")
		}

		if origVolatileApplyTemplate != "" {
			config := map[string]string{
				"volatile.apply_template": origVolatileApplyTemplate,
			}
			err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
				return tx.ContainerConfigInsert(id, config)
			})
			if err != nil {
				return errors.Wrap(err, "Failed to set volatile.apply_template config key")
			}
		}

		return nil
	}

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

	return OperationResponse(op)
}
func containerPostClusteringMigrate(d *Daemon, c container, oldName, newName, newNode string) Response {
	cert := d.endpoints.NetworkCert()

	var sourceAddress string
	var targetAddress string

	// Save the original value of the "volatile.apply_template" config key,
	// since we'll want to preserve it in the copied container.
	origVolatileApplyTemplate := c.LocalConfig()["volatile.apply_template"]

	err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
		var err error

		sourceAddress, err = tx.NodeAddress()
		if err != nil {
			return errors.Wrap(err, "Failed to get local node address")
		}

		node, err := tx.NodeByName(newNode)
		if err != nil {
			return errors.Wrap(err, "Failed to get new node address")
		}
		targetAddress = node.Address

		return nil
	})
	if err != nil {
		return SmartError(err)
	}

	run := func(*operation) error {
		// Connect to the source host, i.e. ourselves (the node the container is running on).
		source, err := cluster.Connect(sourceAddress, cert, false)
		if err != nil {
			return errors.Wrap(err, "Failed to connect to source server")
		}

		// Connect to the destination host, i.e. the node to migrate the container to.
		dest, err := cluster.Connect(targetAddress, cert, false)
		if err != nil {
			return errors.Wrap(err, "Failed to connect to destination server")
		}
		dest = dest.UseTarget(newNode)

		destName := newName
		isSameName := false

		// If no new name was provided, the user wants to keep the same
		// container name. In that case we need to generate a temporary
		// name.
		if destName == "" || destName == oldName {
			isSameName = true
			destName = fmt.Sprintf("move-%s", uuid.NewRandom().String())
		}

		// First make a copy on the new node of the container to be moved.
		entry, _, err := source.GetContainer(oldName)
		if err != nil {
			return errors.Wrap(err, "Failed to get container info")
		}

		args := lxd.ContainerCopyArgs{
			Name: destName,
			Mode: "pull",
		}

		copyOp, err := dest.CopyContainer(source, *entry, &args)
		if err != nil {
			return errors.Wrap(err, "Failed to issue copy container API request")
		}

		err = copyOp.Wait()
		if err != nil {
			return errors.Wrap(err, "Copy container operation failed")
		}

		// Delete the container on the original node.
		deleteOp, err := source.DeleteContainer(oldName)
		if err != nil {
			return errors.Wrap(err, "Failed to issue delete container API request")
		}

		err = deleteOp.Wait()
		if err != nil {
			return errors.Wrap(err, "Delete container operation failed")
		}

		// If the destination name is not set, we have generated a random name for
		// the new container, so we need to rename it.
		if isSameName {
			containerPost := api.ContainerPost{
				Name: oldName,
			}

			op, err := dest.RenameContainer(destName, containerPost)
			if err != nil {
				return errors.Wrap(err, "Failed to issue rename container API request")
			}

			err = op.Wait()
			if err != nil {
				return errors.Wrap(err, "Rename container operation failed")
			}
			destName = oldName
		}

		// Restore the original value of "volatile.apply_template"
		id, err := d.cluster.ContainerID(destName)
		if err != nil {
			return errors.Wrap(err, "Failed to get ID of moved container")
		}

		err = d.cluster.ContainerConfigRemove(id, "volatile.apply_template")
		if err != nil {
			return errors.Wrap(err, "Failed to remove volatile.apply_template config key")
		}

		if origVolatileApplyTemplate != "" {
			config := map[string]string{
				"volatile.apply_template": origVolatileApplyTemplate,
			}
			err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
				return tx.ContainerConfigInsert(id, config)
			})
			if err != nil {
				return errors.Wrap(err, "Failed to set volatile.apply_template config key")
			}
		}

		return nil
	}

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

	return OperationResponse(op)
}
// Move a non-ceph container to another cluster node.