lxd/storage_volumes.go
func storagePoolVolumeTypePut(d *Daemon, r *http.Request, volumeTypeName string) Response {
	// Get the name of the storage volume.
	var volumeName string
	fields := strings.Split(mux.Vars(r)["name"], "/")

	if len(fields) == 3 && fields[1] == "snapshots" {
		// Handle volume snapshots
		volumeName = fmt.Sprintf("%s%s%s", fields[0], shared.SnapshotDelimiter, fields[2])
	} else if len(fields) > 1 {
		volumeName = fmt.Sprintf("%s%s%s", fields[0], shared.SnapshotDelimiter, fields[1])
	} else if len(fields) > 0 {
		// Handle volume
		volumeName = fields[0]
	} else {
		return BadRequest(fmt.Errorf("invalid storage volume %s", mux.Vars(r)["name"]))
	}

	// Get the name of the storage pool the volume is supposed to be
	// attached to.
	poolName := mux.Vars(r)["pool"]

	// Convert the volume type name to our internal integer representation.
	volumeType, err := storagePoolVolumeTypeNameToType(volumeTypeName)
	if err != nil {
		return BadRequest(err)
	}
	// Check that the storage volume type is valid.
	if !shared.IntInSlice(volumeType, supportedVolumeTypes) {
		return BadRequest(fmt.Errorf("invalid storage volume type %s", volumeTypeName))
	}

	poolID, pool, err := d.cluster.StoragePoolGet(poolName)
	if err != nil {
		return SmartError(err)
	}

	response := ForwardedResponseIfTargetIsRemote(d, r)
	if response != nil {
		return response
	}

	response = ForwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, volumeType)
	if response != nil {
		return response
	}

	// Get the existing storage volume.
	_, volume, err := d.cluster.StoragePoolNodeVolumeGetType(volumeName, volumeType, poolID)
	if err != nil {
		return SmartError(err)
	}

	// Validate the ETag
	etag := []interface{}{volumeName, volume.Type, volume.Config}

	err = util.EtagCheck(r, etag)
	if err != nil {
		return PreconditionFailed(err)
	}

	req := api.StorageVolumePut{}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		return BadRequest(err)
	}

	if req.Restore != "" {
		ctsUsingVolume, err := storagePoolVolumeUsedByRunningContainersWithProfilesGet(d.State(), poolName, volume.Name, storagePoolVolumeTypeNameCustom, true)
		if err != nil {
			return InternalError(err)
		}

		if len(ctsUsingVolume) != 0 {
			return BadRequest(fmt.Errorf("Cannot restore custom volume used by running containers"))
		}

		err = storagePoolVolumeRestore(d.State(), poolName, volumeName, volumeType, req.Restore)
		if err != nil {
			return SmartError(err)
		}
	} else {
		// Validate the configuration
		err = storageVolumeValidateConfig(volumeName, req.Config, pool)
		if err != nil {
			return BadRequest(err)
		}

		err = storagePoolVolumeUpdate(d.State(), poolName, volumeName, volumeType, req.Description, req.Config)
		if err != nil {
			return SmartError(err)
		}
	}

	return EmptySyncResponse
}
func storagePoolVolumeTypePut(d *Daemon, r *http.Request, volumeTypeName string) Response {
	// Get the name of the storage volume.
	var volumeName string
	fields := strings.Split(mux.Vars(r)["name"], "/")

	if len(fields) == 3 && fields[1] == "snapshots" {
		// Handle volume snapshots
		volumeName = fmt.Sprintf("%s%s%s", fields[0], shared.SnapshotDelimiter, fields[2])
	} else if len(fields) > 1 {
		volumeName = fmt.Sprintf("%s%s%s", fields[0], shared.SnapshotDelimiter, fields[1])
	} else if len(fields) > 0 {
		// Handle volume
		volumeName = fields[0]
	} else {
		return BadRequest(fmt.Errorf("invalid storage volume %s", mux.Vars(r)["name"]))
	}

	// Get the name of the storage pool the volume is supposed to be
	// attached to.
	poolName := mux.Vars(r)["pool"]

	// Convert the volume type name to our internal integer representation.
	volumeType, err := storagePoolVolumeTypeNameToType(volumeTypeName)
	if err != nil {
		return BadRequest(err)
	}
	// Check that the storage volume type is valid.
	if !shared.IntInSlice(volumeType, supportedVolumeTypes) {
		return BadRequest(fmt.Errorf("invalid storage volume type %s", volumeTypeName))
	}

	poolID, pool, err := d.cluster.StoragePoolGet(poolName)
	if err != nil {
		return SmartError(err)
	}

	response := ForwardedResponseIfTargetIsRemote(d, r)
	if response != nil {
		return response
	}

	response = ForwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, volumeType)
	if response != nil {
		return response
	}

	// Get the existing storage volume.
	_, volume, err := d.cluster.StoragePoolNodeVolumeGetType(volumeName, volumeType, poolID)
	if err != nil {
		return SmartError(err)
	}

	// Validate the ETag
	etag := []interface{}{volumeName, volume.Type, volume.Config}

	err = util.EtagCheck(r, etag)
	if err != nil {
		return PreconditionFailed(err)
	}

	req := api.StorageVolumePut{}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		return BadRequest(err)
	}

	if req.Restore != "" {
		ctsUsingVolume, err := storagePoolVolumeUsedByRunningContainersWithProfilesGet(d.State(), poolName, volume.Name, storagePoolVolumeTypeNameCustom, true)
		if err != nil {
			return InternalError(err)
		}

		if len(ctsUsingVolume) != 0 {
			return BadRequest(fmt.Errorf("Cannot restore custom volume used by running containers"))
		}

		err = storagePoolVolumeRestore(d.State(), poolName, volumeName, volumeType, req.Restore)
		if err != nil {
			return SmartError(err)
		}
	} else {
		// Validate the configuration
		err = storageVolumeValidateConfig(volumeName, req.Config, pool)
		if err != nil {
			return BadRequest(err)
		}

		err = storagePoolVolumeUpdate(d.State(), poolName, volumeName, volumeType, req.Description, req.Config)
		if err != nil {
			return SmartError(err)
		}
	}

	return EmptySyncResponse
}
// /1.0/storage-pools/{pool}/volumes/{type}/{name}