mirror of
https://github.com/lxc/incus
synced 2026-08-02 05:26:46 +00:00
201 lines
5.9 KiB
Go
201 lines
5.9 KiB
Go
//go:build linux
|
|
|
|
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
|
|
incus "github.com/lxc/incus/v7/client"
|
|
"github.com/lxc/incus/v7/internal/i18n"
|
|
"github.com/lxc/incus/v7/internal/linux"
|
|
"github.com/lxc/incus/v7/internal/ports"
|
|
internalUtil "github.com/lxc/incus/v7/internal/util"
|
|
"github.com/lxc/incus/v7/shared/api"
|
|
"github.com/lxc/incus/v7/shared/util"
|
|
)
|
|
|
|
func (c *cmdAdminInit) runAuto(d incus.InstanceServer, server *api.Server) (*api.InitPreseed, error) {
|
|
// Quick checks.
|
|
if c.flagStorageBackend != "" && !slices.Contains([]string{"dir", "btrfs", "lvm", "zfs"}, c.flagStorageBackend) {
|
|
return nil, fmt.Errorf(i18n.G("The requested backend '%s' isn't supported by init"), c.flagStorageBackend)
|
|
}
|
|
|
|
if c.flagStorageBackend != "" && !slices.Contains(linux.AvailableStorageDrivers(internalUtil.VarPath(), server.Environment.StorageSupportedDrivers, internalUtil.PoolTypeAny), c.flagStorageBackend) {
|
|
return nil, fmt.Errorf(i18n.G("The requested backend '%s' isn't available on your system (missing tools)"), c.flagStorageBackend)
|
|
}
|
|
|
|
if c.flagStorageBackend == "dir" || c.flagStorageBackend == "" {
|
|
if c.flagStorageLoopSize != -1 || c.flagStorageDevice != "" || c.flagStoragePool != "" {
|
|
return nil, errors.New(i18n.G("None of --storage-pool, --storage-create-device or --storage-create-loop may be used with the 'dir' backend"))
|
|
}
|
|
} else {
|
|
if c.flagStorageLoopSize != -1 && c.flagStorageDevice != "" {
|
|
return nil, errors.New(i18n.G("Only one of --storage-create-device or --storage-create-loop can be specified"))
|
|
}
|
|
}
|
|
|
|
if c.flagNetworkAddress == "" {
|
|
if c.flagNetworkPort != -1 {
|
|
return nil, errors.New(i18n.G("--network-port can't be used without --network-address"))
|
|
}
|
|
}
|
|
|
|
storagePools, err := d.GetStoragePoolNames()
|
|
if err != nil {
|
|
return nil, fmt.Errorf(i18n.G("Failed to retrieve list of storage pools: %w"), err)
|
|
}
|
|
|
|
if len(storagePools) > 0 && (c.flagStorageBackend != "" || c.flagStorageDevice != "" || c.flagStorageLoopSize != -1 || c.flagStoragePool != "") {
|
|
return nil, errors.New(i18n.G("Storage has already been configured"))
|
|
}
|
|
|
|
// Detect the backing filesystem.
|
|
backingFs, err := linux.DetectFilesystem(internalUtil.VarPath())
|
|
if err != nil {
|
|
backingFs = "dir"
|
|
}
|
|
|
|
// Get the possible local storage drivers.
|
|
storageDrivers := linux.AvailableStorageDrivers(internalUtil.VarPath(), server.Environment.StorageSupportedDrivers, internalUtil.PoolTypeLocal)
|
|
|
|
// Defaults
|
|
if c.flagNetworkPort == -1 {
|
|
c.flagNetworkPort = ports.HTTPSDefaultPort
|
|
}
|
|
|
|
if c.flagStorageBackend == "" && c.flagStoragePool == "" && backingFs == "btrfs" && slices.Contains(storageDrivers, "btrfs") {
|
|
// Use btrfs subvol if running on btrfs.
|
|
c.flagStoragePool = internalUtil.VarPath("storage-pools", "default")
|
|
c.flagStorageBackend = "btrfs"
|
|
} else if c.flagStorageBackend == "" {
|
|
c.flagStorageBackend = "dir"
|
|
}
|
|
|
|
// Fill in the node configuration
|
|
config := api.InitLocalPreseed{}
|
|
config.Config = map[string]string{}
|
|
|
|
// Network listening
|
|
if c.flagNetworkAddress != "" {
|
|
config.Config["core.https_address"] = internalUtil.CanonicalNetworkAddressFromAddressAndPort(c.flagNetworkAddress, c.flagNetworkPort, ports.HTTPSDefaultPort)
|
|
}
|
|
|
|
// Storage configuration
|
|
if len(storagePools) == 0 {
|
|
// Storage pool
|
|
pool := api.StoragePoolsPost{
|
|
Name: "default",
|
|
Driver: c.flagStorageBackend,
|
|
}
|
|
|
|
pool.Config = map[string]string{}
|
|
|
|
if c.flagStorageDevice != "" {
|
|
pool.Config["source"] = c.flagStorageDevice
|
|
} else if c.flagStorageLoopSize > 0 {
|
|
pool.Config["size"] = fmt.Sprintf("%dGiB", c.flagStorageLoopSize)
|
|
} else {
|
|
pool.Config["source"] = c.flagStoragePool
|
|
}
|
|
|
|
// If using a device or loop, --storage-pool refers to the name of the new pool
|
|
if c.flagStoragePool != "" && (c.flagStorageDevice != "" || c.flagStorageLoopSize != -1) {
|
|
pool.Name = c.flagStoragePool
|
|
}
|
|
|
|
config.StoragePools = []api.StoragePoolsPost{pool}
|
|
|
|
// Profile entry
|
|
config.Profiles = []api.InitProfileProjectPost{{
|
|
ProfilesPost: api.ProfilesPost{
|
|
Name: "default",
|
|
ProfilePut: api.ProfilePut{
|
|
Devices: map[string]map[string]string{
|
|
"root": {
|
|
"type": "disk",
|
|
"path": "/",
|
|
"pool": pool.Name,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Project: api.ProjectDefaultName,
|
|
}}
|
|
}
|
|
|
|
// Network configuration
|
|
networks, err := d.GetNetworks()
|
|
if err != nil {
|
|
return nil, fmt.Errorf(i18n.G("Failed to retrieve list of networks: %w"), err)
|
|
}
|
|
|
|
// Extract managed networks
|
|
managedNetworks := []api.Network{}
|
|
for _, network := range networks {
|
|
if network.Managed {
|
|
managedNetworks = append(managedNetworks, network)
|
|
}
|
|
}
|
|
|
|
// Look for an existing network device in the profile
|
|
defaultProfileNetwork := false
|
|
defaultProfile, _, err := d.GetProfile("default")
|
|
if err == nil {
|
|
for _, dev := range defaultProfile.Devices {
|
|
if dev["type"] == "nic" {
|
|
defaultProfileNetwork = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Define a new network
|
|
if len(managedNetworks) == 0 && !defaultProfileNetwork {
|
|
// Find a new name
|
|
idx := 0
|
|
for {
|
|
if util.PathExists(fmt.Sprintf("/sys/class/net/incusbr%d", idx)) {
|
|
idx++
|
|
continue
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
// Define the new network
|
|
network := api.InitNetworksProjectPost{}
|
|
network.Name = fmt.Sprintf("incusbr%d", idx)
|
|
network.Project = api.ProjectDefaultName
|
|
config.Networks = append(config.Networks, network)
|
|
|
|
// Add it to the profile
|
|
if config.Profiles == nil {
|
|
config.Profiles = []api.InitProfileProjectPost{{
|
|
ProfilesPost: api.ProfilesPost{
|
|
Name: "default",
|
|
ProfilePut: api.ProfilePut{
|
|
Devices: map[string]map[string]string{
|
|
"eth0": {
|
|
"type": "nic",
|
|
"network": network.Name,
|
|
"name": "eth0",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Project: api.ProjectDefaultName,
|
|
}}
|
|
} else {
|
|
config.Profiles[0].Devices["eth0"] = map[string]string{
|
|
"type": "nic",
|
|
"network": network.Name,
|
|
"name": "eth0",
|
|
}
|
|
}
|
|
}
|
|
|
|
return &api.InitPreseed{InitLocalPreseed: config}, nil
|
|
}
|