incus-mirror/cmd/incusd/storage_pools_utils.go
Stéphane Graber 40dd4f151d
Rewrite Go import path to v7
Signed-off-by: Stéphane Graber <stgraber@stgraber.org>
2026-05-02 01:18:25 +02:00

194 lines
5.8 KiB
Go

package main
import (
"context"
"fmt"
"github.com/lxc/incus/v7/internal/server/cluster/request"
"github.com/lxc/incus/v7/internal/server/db"
"github.com/lxc/incus/v7/internal/server/state"
storagePools "github.com/lxc/incus/v7/internal/server/storage"
"github.com/lxc/incus/v7/shared/api"
"github.com/lxc/incus/v7/shared/logger"
"github.com/lxc/incus/v7/shared/revert"
"github.com/lxc/incus/v7/shared/validate"
)
// storagePoolDBCreate creates a storage pool DB entry and returns the created Pool ID.
func storagePoolDBCreate(ctx context.Context, s *state.State, poolName string, poolDescription string, driver string, config map[string]string) (int64, error) {
err := s.DB.Cluster.Transaction(ctx, func(ctx context.Context, tx *db.ClusterTx) error {
// Check that the storage pool does not already exist.
_, err := tx.GetStoragePoolID(ctx, poolName)
return err
})
if err == nil {
return -1, fmt.Errorf("The storage pool already exists: %w", db.ErrAlreadyDefined)
}
// Make sure that we don't pass a nil to the next function.
if config == nil {
config = map[string]string{}
}
err = storagePoolValidate(s, poolName, driver, config)
if err != nil {
return -1, err
}
// Create the database entry for the storage pool.
id, err := dbStoragePoolCreateAndUpdateCache(ctx, s, poolName, poolDescription, driver, config)
if err != nil {
return -1, fmt.Errorf("Error inserting %s into database: %w", poolName, err)
}
return id, nil
}
func storagePoolValidate(s *state.State, poolName string, driverName string, config map[string]string) error {
poolType, err := storagePools.LoadByType(s, driverName)
if err != nil {
return err
}
// Check if the storage pool name is valid.
err = validate.IsAPIName(poolName, false)
if err != nil {
return err
}
// Validate the requested storage pool configuration.
err = poolType.Validate(config)
if err != nil {
return err
}
return nil
}
func storagePoolCreateGlobal(ctx context.Context, s *state.State, req api.StoragePoolsPost, clientType request.ClientType) error {
// Create the database entry.
id, err := storagePoolDBCreate(ctx, s, req.Name, req.Description, req.Driver, req.Config)
if err != nil {
return err
}
// Define a function which reverts everything. Defer this function
// so that it doesn't need to be explicitly called in every failing
// return path. Track whether or not we want to undo the changes
// using a closure.
reverter := revert.New()
defer reverter.Fail()
reverter.Add(func() { _ = dbStoragePoolDeleteAndUpdateCache(context.Background(), s, req.Name) })
_, err = storagePoolCreateLocal(ctx, s, id, req, clientType)
if err != nil {
return err
}
reverter.Success()
return nil
}
// This performs local pool setup and updates DB record if config was changed during pool setup.
// Returns resulting config.
func storagePoolCreateLocal(ctx context.Context, s *state.State, poolID int64, req api.StoragePoolsPost, clientType request.ClientType) (map[string]string, error) {
// Setup revert.
reverter := revert.New()
defer reverter.Fail()
// Load pool record.
pool, err := storagePools.LoadByName(s, req.Name)
if err != nil {
return nil, err
}
if pool.LocalStatus() == api.NetworkStatusCreated {
logger.Debug("Skipping local storage pool create as already created", logger.Ctx{"pool": pool.Name()})
return pool.Driver().Config(), nil
}
// Create the pool.
err = pool.Create(clientType, nil)
if err != nil {
return nil, err
}
reverter.Add(func() { _ = pool.Delete(clientType, nil) })
// Mount the pool.
_, err = pool.Mount()
if err != nil {
return nil, err
}
// In case the storage pool config was changed during the pool creation, we need to update the database to
// reflect this change. This can e.g. happen, when we create a loop file image. This means we append ".img"
// to the path the user gave us and update the config in the storage callback. So diff the config here to
// see if something like this has happened.
configDiff, _ := storagePools.ConfigDiff(req.Config, pool.Driver().Config())
if len(configDiff) > 0 {
err = s.DB.Cluster.Transaction(ctx, func(ctx context.Context, tx *db.ClusterTx) error {
// Update the database entry for the storage pool.
return tx.UpdateStoragePool(ctx, req.Name, req.Description, pool.Driver().Config())
})
if err != nil {
return nil, fmt.Errorf("Error updating storage pool config after local create for %q: %w", req.Name, err)
}
}
// Set storage pool node to storagePoolCreated.
err = s.DB.Cluster.Transaction(ctx, func(ctx context.Context, tx *db.ClusterTx) error {
return tx.StoragePoolNodeCreated(poolID)
})
if err != nil {
return nil, err
}
logger.Debug("Marked storage pool local status as created", logger.Ctx{"pool": req.Name})
reverter.Success()
return pool.Driver().Config(), nil
}
// Helper around the low-level DB API, which also updates the driver names cache.
func dbStoragePoolCreateAndUpdateCache(ctx context.Context, s *state.State, poolName string, poolDescription string, poolDriver string, poolConfig map[string]string) (int64, error) {
var id int64
err := s.DB.Cluster.Transaction(ctx, func(ctx context.Context, tx *db.ClusterTx) error {
var err error
id, err = tx.CreateStoragePool(ctx, poolName, poolDescription, poolDriver, poolConfig)
return err
})
if err != nil {
return id, err
}
// Update the storage drivers cache in api_1.0.go.
storagePoolDriversCacheUpdate(ctx, s)
return id, nil
}
// Helper around the low-level DB API, which also updates the driver names
// cache.
func dbStoragePoolDeleteAndUpdateCache(ctx context.Context, s *state.State, poolName string) error {
err := s.DB.Cluster.Transaction(ctx, func(ctx context.Context, tx *db.ClusterTx) error {
_, err := tx.RemoveStoragePool(ctx, poolName)
return err
})
if err != nil {
return err
}
// Update the storage drivers cache in api_1.0.go.
storagePoolDriversCacheUpdate(ctx, s)
return err
}