Skip to content

Commit

Permalink
incus/init: Add support for storage volumes in preseed init
Browse files Browse the repository at this point in the history
Add support for configuring storage volumes in preseed init.
  • Loading branch information
megheaiulian committed Jan 21, 2025
1 parent 737cb4d commit c867179
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 0 deletions.
64 changes: 64 additions & 0 deletions client/incus_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"github.com/gorilla/websocket"

"github.com/lxc/incus/v6/shared/api"
"github.com/lxc/incus/v6/shared/util"

Check failure on line 13 in client/incus_server.go

View workflow job for this annotation

GitHub Actions / Code (oldstable)

File is not properly formatted (gci)

Check failure on line 13 in client/incus_server.go

View workflow job for this annotation

GitHub Actions / Code (stable)

File is not properly formatted (gci)

Check failure on line 13 in client/incus_server.go

View workflow job for this annotation

GitHub Actions / Code (tip)

File is not properly formatted (gci)
localtls "github.com/lxc/incus/v6/shared/tls"
)

Expand Down Expand Up @@ -405,6 +407,68 @@ func (r *ProtocolIncus) ApplyServerPreseed(config api.InitPreseed) error {
}
}

// Apply storage volumes configuration.
applyStorageVolume := func(storageVolume api.InitStorageVolumesProjectPost) error {
// Get the current storageVolume.
currentStorageVolume, etag, err := r.UseProject(storageVolume.Project).GetStoragePoolVolume(storageVolume.Pool, storageVolume.Type, storageVolume.Name)

if err != nil {
// Create the storage volume if it doesn't exist.
err := r.UseProject(storageVolume.Project).CreateStoragePoolVolume(storageVolume.Pool, storageVolume.StorageVolumesPost)
if err != nil {
return fmt.Errorf("Failed to create storage volume %q in project %q on pool %q: %w", storageVolume.Name, storageVolume.Project, storageVolume.Pool, err)
}
} else {
// Quick check.
if currentStorageVolume.Type != storageVolume.Type {
return fmt.Errorf("Storage volume %q in project %q is of type %q instead of %q", currentStorageVolume.Name, storageVolume.Project, currentStorageVolume.Type, storageVolume.Type)
}

// Prepare the update.
newStorageVolume := api.StorageVolumePut{}
err = util.DeepCopy(currentStorageVolume.Writable(), &newStorageVolume)
if err != nil {
return fmt.Errorf("Failed to copy configuration of storage volume %q in project %q: %w", storageVolume.Name, storageVolume.Project, err)
}

// Description override.
if storageVolume.Description != "" {
newStorageVolume.Description = storageVolume.Description
}

// Config overrides.
for k, v := range storageVolume.Config {
newStorageVolume.Config[k] = fmt.Sprintf("%v", v)
}

// Apply it.
err = r.UseProject(storageVolume.Project).UpdateStoragePoolVolume(storageVolume.Pool, storageVolume.Type, currentStorageVolume.Name, newStorageVolume, etag)
if err != nil {
return fmt.Errorf("Failed to update storage volume %q in project %q: %w", storageVolume.Name, storageVolume.Project, err)
}
}

return nil
}

// Apply storage volumes in the default project before other projects config.
for i := range config.Server.StorageVolumes {

Check failure on line 455 in client/incus_server.go

View workflow job for this annotation

GitHub Actions / Code (oldstable)

empty-lines: extra empty line at the end of a block (revive)

Check failure on line 455 in client/incus_server.go

View workflow job for this annotation

GitHub Actions / Code (stable)

empty-lines: extra empty line at the end of a block (revive)

Check failure on line 455 in client/incus_server.go

View workflow job for this annotation

GitHub Actions / Code (tip)

empty-lines: extra empty line at the end of a block (revive)
// Populate default project if not specified.
if config.Server.StorageVolumes[i].Project == "" {
config.Server.StorageVolumes[i].Project = api.ProjectDefaultName
}
// Populate default type if not specified.
if config.Server.StorageVolumes[i].Type == "" {
config.Server.StorageVolumes[i].Type = "custom"
}

err := applyStorageVolume(config.Server.StorageVolumes[i])
if err != nil {
return err
}

}

Check failure on line 470 in client/incus_server.go

View workflow job for this annotation

GitHub Actions / Code (oldstable)

unnecessary trailing newline (whitespace)

Check failure on line 470 in client/incus_server.go

View workflow job for this annotation

GitHub Actions / Code (stable)

unnecessary trailing newline (whitespace)

Check failure on line 470 in client/incus_server.go

View workflow job for this annotation

GitHub Actions / Code (tip)

unnecessary trailing newline (whitespace)

// Apply profile configuration.
if config.Server.Profiles != nil && len(config.Server.Profiles) > 0 {
// Get the list of profiles.
Expand Down
3 changes: 3 additions & 0 deletions doc/api-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2689,3 +2689,6 @@ This extends the QEMU scriptlet feature by allowing to modify QEMU configuration
## `network_bridge_acl_devices`

This adds support for device ACLs when attached to a bridged network.

## `init_preseed_storage_volumes`
This API extension provides the ability to configure storage volumes in preseed init.
5 changes: 5 additions & 0 deletions doc/howto/initialize.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ storage_pools:
config:
source: my-zfs-pool/my-zfs-dataset

# Storage volumes
storage_volumes:
- name: my-vol
pool: data

# Network devices
networks:
- name: incus-my-bridge
Expand Down
56 changes: 56 additions & 0 deletions doc/rest-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,13 @@ definitions:
$ref: '#/definitions/StoragePoolsPost'
type: array
x-go-name: StoragePools
storage_volumes:
description: Storage Volumes to add
example: local dir storage volume
items:
$ref: '#/definitions/InitStorageVolumesProjectPost'
type: array
x-go-name: StorageVolumes
title: InitLocalPreseed represents initialization configuration.
type: object
x-go-package: github.com/lxc/incus/v6/shared/api
Expand Down Expand Up @@ -1317,6 +1324,55 @@ definitions:
title: InitPreseed represents initialization configuration that can be supplied to `init`.
type: object
x-go-package: github.com/lxc/incus/v6/shared/api
InitStorageVolumesProjectPost:
properties:
Pool:
description: Storage pool in which the volume will reside
example: '"default"'
type: string
Project:
description: Project in which the volume will reside
example: '"default"'
type: string
config:
additionalProperties:
type: string
description: Storage volume configuration map (refer to doc/storage.md)
example:
size: 50GiB
zfs.remove_snapshots: "true"
type: object
x-go-name: Config
content_type:
description: Volume content type (filesystem or block)
example: filesystem
type: string
x-go-name: ContentType
description:
description: Description of the storage volume
example: My custom volume
type: string
x-go-name: Description
name:
description: Volume name
example: foo
type: string
x-go-name: Name
restore:
description: Name of a snapshot to restore
example: snap0
type: string
x-go-name: Restore
source:
$ref: '#/definitions/StorageVolumeSource'
type:
description: Volume type (container, custom, image or virtual-machine)
example: custom
type: string
x-go-name: Type
title: InitStorageVolumesProjectPost represents the fields of a new storage volume along with its associated pool.
type: object
x-go-package: github.com/lxc/incus/v6/shared/api
Instance:
properties:
architecture:
Expand Down
1 change: 1 addition & 0 deletions internal/version/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ var APIExtensions = []string{
"console_force",
"network_ovn_state_addresses",
"network_bridge_acl_devices",
"init_preseed_storage_volumes",
}

// APIExtensionsCount returns the number of available API extensions.
Expand Down
19 changes: 19 additions & 0 deletions shared/api/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ type InitLocalPreseed struct {
// Example: local dir storage pool
StoragePools []StoragePoolsPost `json:"storage_pools" yaml:"storage_pools"`

// Storage Volumes to add
// Example: local dir storage volume
StorageVolumes []InitStorageVolumesProjectPost `json:"storage_volumes" yaml:"storage_volumes"`

// Profiles to add
// Example: "default" profile with a root disk device
Profiles []ProfilesPost `json:"profiles" yaml:"profiles"`
Expand All @@ -48,6 +52,21 @@ type InitNetworksProjectPost struct {
Project string
}

// InitStorageVolumesProjectPost represents the fields of a new storage volume along with its associated pool.
//
// swagger:model
//
// API extension: init_preseed_storage_volumes.
type InitStorageVolumesProjectPost struct {
StorageVolumesPost `yaml:",inline"`
// Storage pool in which the volume will reside
// Example: "default"
Pool string
// Project in which the volume will reside
// Example: "default"
Project string
}

// InitClusterPreseed represents initialization configuration for the cluster.
//
// swagger:model
Expand Down
6 changes: 6 additions & 0 deletions test/suites/init_preseed.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ test_init_preseed() {
INCUS_DIR=${INCUS_INIT_DIR}

storage_pool="incustest-$(basename "${INCUS_DIR}")-data"
storage_volume="${storage_pool}-volume"
# In case we're running against the ZFS backend, let's test
# creating a zfs storage pool, otherwise just use dir.
if [ "$incus_backend" = "zfs" ]; then
Expand All @@ -36,6 +37,9 @@ storage_pools:
driver: $driver
config:
source: $source
storage_volumes:
- name: ${storage_volume}
pool: ${storage_pool}
networks:
- name: inct$$
type: bridge
Expand Down Expand Up @@ -66,6 +70,7 @@ EOF
incus network list | grep -q "inct$$"
incus storage list | grep -q "${storage_pool}"
incus storage show "${storage_pool}" | grep -q "$source"
incus storage volume list "${storage_pool}" | grep -q "${storage_volume}"
incus profile list | grep -q "test-profile"
incus profile show default | grep -q "pool: ${storage_pool}"
incus profile show test-profile | grep -q "limits.memory: 2GiB"
Expand All @@ -74,6 +79,7 @@ EOF
printf 'config: {}\ndevices: {}' | incus profile edit default
incus profile delete test-profile
incus network delete inct$$
incus storage volume delete "${storage_pool}" "${storage_volume}"
incus storage delete "${storage_pool}"

if [ "$incus_backend" = "zfs" ]; then
Expand Down

0 comments on commit c867179

Please sign in to comment.