diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index eb27493eeb18..9d891b71d68f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -320,6 +320,7 @@ jobs: sudo umount /mnt sudo snap install microceph --edge + sudo snap connect microceph:mount-observe sudo apt-get install --no-install-recommends -y ceph-common sudo microceph cluster bootstrap sudo microceph.ceph config set global osd_pool_default_size 1 diff --git a/lxd/api_internal.go b/lxd/api_internal.go index 5380033a0c29..b674f1152fed 100644 --- a/lxd/api_internal.go +++ b/lxd/api_internal.go @@ -590,7 +590,8 @@ func internalSQLExec(tx *sql.Tx, query string, result *internalSQLResult) error // internalImportFromBackup creates instance, storage pool and volume DB records from an instance's backup file. // It expects the instance volume to be mounted so that the backup.yaml file is readable. -func internalImportFromBackup(s *state.State, projectName string, instName string, allowNameOverride bool, devices map[string]map[string]string) error { +// Also accepts an optional map of device overrides. +func internalImportFromBackup(s *state.State, projectName string, instName string, allowNameOverride bool, deviceOverrides map[string]map[string]string) error { if instName == "" { return fmt.Errorf("The name of the instance is required") } @@ -663,28 +664,6 @@ func internalImportFromBackup(s *state.State, projectName string, instName strin return err } - // Override instance devices. - if len(devices) > 0 { - if backupConf.Container.Devices == nil { - backupConf.Container.Devices = map[string]map[string]string{} - } - - for devName, devConfig := range devices { - if backupConf.Container.Devices[devName] == nil { - backupConf.Container.Devices[devName] = map[string]string{} - } - - for k, v := range devConfig { - backupConf.Container.Devices[devName][k] = v - } - } - - err = backup.OverrideConfigYamlFile(backupYamlPath, backupConf) - if err != nil { - return err - } - } - if allowNameOverride && instName != "" { backupConf.Container.Name = instName } @@ -695,7 +674,7 @@ func internalImportFromBackup(s *state.State, projectName string, instName strin if backupConf.Pool == nil { // We don't know what kind of storage type the pool is. - return fmt.Errorf(`No storage pool struct in the backup file found. The storage pool needs to be recovered manually`) + return fmt.Errorf("No storage pool struct in the backup file found. The storage pool needs to be recovered manually") } // Try to retrieve the storage pool the instance supposedly lives on. @@ -794,7 +773,7 @@ func internalImportFromBackup(s *state.State, projectName string, instName strin return fmt.Errorf("Failed loading profiles for instance: %w", err) } - // Add root device if needed. + // Initialise the devices maps. if backupConf.Container.Devices == nil { backupConf.Container.Devices = make(map[string]map[string]string, 0) } @@ -803,6 +782,33 @@ func internalImportFromBackup(s *state.State, projectName string, instName strin backupConf.Container.ExpandedDevices = make(map[string]map[string]string, 0) } + // Apply device overrides. + // Do this before calling internalImportRootDevicePopulate so that device overrides are taken into account. + for deviceName := range deviceOverrides { + _, isLocalDevice := backupConf.Container.Devices[deviceName] + if isLocalDevice { + // Apply overrides to local device. + for k, v := range deviceOverrides[deviceName] { + backupConf.Container.Devices[deviceName][k] = v + } + } else { + // Check device exists in expanded profile devices. + profileDeviceConfig, found := backupConf.Container.ExpandedDevices[deviceName] + if !found { + return fmt.Errorf("Cannot override config for device %q: Device not found in profile devices", deviceName) + } + + for k, v := range deviceOverrides[deviceName] { + profileDeviceConfig[k] = v + } + + // Add device to local devices. + backupConf.Container.Devices[deviceName] = profileDeviceConfig + } + } + + // Add root device if needed. + // And ensure root device is associated with same pool as instance has been imported to. internalImportRootDevicePopulate(instancePoolName, backupConf.Container.Devices, backupConf.Container.ExpandedDevices, profiles) revert := revert.New() @@ -972,7 +978,7 @@ func internalImportRootDevicePopulate(instancePoolName string, localDevices map[ expandedRootName, expandedRootConfig, _ := instancetype.GetRootDiskDevice(expandedDevices) // Extract root disk from expanded profile devices. - profileExpandedDevices := db.ExpandInstanceDevices(deviceConfig.NewDevices(localDevices), profiles) + profileExpandedDevices := instancetype.ExpandInstanceDevices(deviceConfig.NewDevices(localDevices), profiles) profileExpandedRootName, profileExpandedRootConfig, _ := instancetype.GetRootDiskDevice(profileExpandedDevices.CloneNative()) // Record whether we need to add a new local disk device. diff --git a/lxd/backup/backup_config_utils.go b/lxd/backup/backup_config_utils.go index 5edb9e235adb..61f5d47de01c 100644 --- a/lxd/backup/backup_config_utils.go +++ b/lxd/backup/backup_config_utils.go @@ -90,33 +90,6 @@ func ParseConfigYamlFile(path string) (*config.Config, error) { return &backupConf, nil } -// OverrideConfigYamlFile overrides the YAML file. -func OverrideConfigYamlFile(path string, backupConf *config.Config) error { - f, err := os.Create(path) - if err != nil { - return err - } - - defer f.Close() - - data, err := yaml.Marshal(backupConf) - if err != nil { - return err - } - - err = f.Chmod(0400) - if err != nil { - return err - } - - _, err = f.Write(data) - if err != nil { - return err - } - - return nil -} - // updateRootDevicePool updates the root disk device in the supplied list of devices to the pool // specified. Returns true if a root disk device has been found and updated otherwise false. func updateRootDevicePool(devices map[string]map[string]string, poolName string) bool { diff --git a/lxd/db/cluster/devices.go b/lxd/db/cluster/devices.go index 411e21551518..7ba4b8f4b073 100644 --- a/lxd/db/cluster/devices.go +++ b/lxd/db/cluster/devices.go @@ -114,7 +114,7 @@ func NewDeviceType(t string) (DeviceType, error) { case "pci": return TypePCI, nil default: - return -1, fmt.Errorf("Invalid device type %s", t) + return -1, fmt.Errorf("Invalid device type %q", t) } } diff --git a/lxd/db/cluster/instances.go b/lxd/db/cluster/instances.go index d1c120963cbf..90e35f928d87 100644 --- a/lxd/db/cluster/instances.go +++ b/lxd/db/cluster/instances.go @@ -101,14 +101,14 @@ func (i *Instance) ToAPI(ctx context.Context, tx *sql.Tx) (*api.Instance, error) } apiDevices := DevicesToAPI(devices) - expandedDevices := ExpandInstanceDevices(config.NewDevices(apiDevices), apiProfiles) + expandedDevices := instancetype.ExpandInstanceDevices(config.NewDevices(apiDevices), apiProfiles) config, err := GetInstanceConfig(ctx, tx, i.ID) if err != nil { return nil, err } - expandedConfig := ExpandInstanceConfig(config, apiProfiles) + expandedConfig := instancetype.ExpandInstanceConfig(config, apiProfiles) archName, err := osarch.ArchitectureName(i.Architecture) if err != nil { diff --git a/lxd/db/cluster/profiles.go b/lxd/db/cluster/profiles.go index 8040414843ae..7e4c8f6fa563 100644 --- a/lxd/db/cluster/profiles.go +++ b/lxd/db/cluster/profiles.go @@ -6,7 +6,6 @@ import ( "context" "database/sql" - "github.com/canonical/lxd/lxd/device/config" "github.com/canonical/lxd/shared/api" ) @@ -99,53 +98,3 @@ func GetProfilesIfEnabled(ctx context.Context, tx *sql.Tx, projectName string, n return profiles, nil } - -// ExpandInstanceConfig expands the given instance config with the config -// values of the given profiles. -func ExpandInstanceConfig(config map[string]string, profiles []api.Profile) map[string]string { - expandedConfig := map[string]string{} - - // Apply all the profiles - profileConfigs := make([]map[string]string, len(profiles)) - for i, profile := range profiles { - profileConfigs[i] = profile.Config - } - - for i := range profileConfigs { - for k, v := range profileConfigs[i] { - expandedConfig[k] = v - } - } - - // Stick the given config on top - for k, v := range config { - expandedConfig[k] = v - } - - return expandedConfig -} - -// ExpandInstanceDevices expands the given instance devices with the devices -// defined in the given profiles. -func ExpandInstanceDevices(devices config.Devices, profiles []api.Profile) config.Devices { - expandedDevices := config.Devices{} - - // Apply all the profiles - profileDevices := make([]config.Devices, len(profiles)) - for i, profile := range profiles { - profileDevices[i] = config.NewDevices(profile.Devices) - } - - for i := range profileDevices { - for k, v := range profileDevices[i] { - expandedDevices[k] = v - } - } - - // Stick the given devices on top - for k, v := range devices { - expandedDevices[k] = v - } - - return expandedDevices -} diff --git a/lxd/db/instances_test.go b/lxd/db/instances_test.go index f1c2e38757ac..197ca645dfb8 100644 --- a/lxd/db/instances_test.go +++ b/lxd/db/instances_test.go @@ -267,8 +267,8 @@ func TestInstanceList(t *testing.T) { err = c.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error { return tx.InstanceList(ctx, func(dbInst db.InstanceArgs, p api.Project) error { - dbInst.Config = db.ExpandInstanceConfig(dbInst.Config, dbInst.Profiles) - dbInst.Devices = db.ExpandInstanceDevices(dbInst.Devices, dbInst.Profiles) + dbInst.Config = instancetype.ExpandInstanceConfig(dbInst.Config, dbInst.Profiles) + dbInst.Devices = instancetype.ExpandInstanceDevices(dbInst.Devices, dbInst.Profiles) instances = append(instances, dbInst) diff --git a/lxd/db/profiles.go b/lxd/db/profiles.go index 6742255292cc..f657cfe8d3bc 100644 --- a/lxd/db/profiles.go +++ b/lxd/db/profiles.go @@ -7,7 +7,6 @@ import ( "fmt" "github.com/canonical/lxd/lxd/db/cluster" - deviceConfig "github.com/canonical/lxd/lxd/device/config" "github.com/canonical/lxd/shared/api" ) @@ -145,53 +144,3 @@ DELETE FROM profiles_devices_config WHERE profile_device_id NOT IN (SELECT id FR return err } - -// ExpandInstanceConfig expands the given instance config with the config -// values of the given profiles. -func ExpandInstanceConfig(config map[string]string, profiles []api.Profile) map[string]string { - expandedConfig := map[string]string{} - - // Apply all the profiles - profileConfigs := make([]map[string]string, len(profiles)) - for i, profile := range profiles { - profileConfigs[i] = profile.Config - } - - for i := range profileConfigs { - for k, v := range profileConfigs[i] { - expandedConfig[k] = v - } - } - - // Stick the given config on top - for k, v := range config { - expandedConfig[k] = v - } - - return expandedConfig -} - -// ExpandInstanceDevices expands the given instance devices with the devices -// defined in the given profiles. -func ExpandInstanceDevices(devices deviceConfig.Devices, profiles []api.Profile) deviceConfig.Devices { - expandedDevices := deviceConfig.Devices{} - - // Apply all the profiles - profileDevices := make([]deviceConfig.Devices, len(profiles)) - for i, profile := range profiles { - profileDevices[i] = deviceConfig.NewDevices(profile.Devices) - } - - for i := range profileDevices { - for k, v := range profileDevices[i] { - expandedDevices[k] = v - } - } - - // Stick the given devices on top - for k, v := range devices { - expandedDevices[k] = v - } - - return expandedDevices -} diff --git a/lxd/endpoints/network_util_test.go b/lxd/endpoints/network_util_test.go index 657bcc0fe744..965bd3010e02 100644 --- a/lxd/endpoints/network_util_test.go +++ b/lxd/endpoints/network_util_test.go @@ -41,25 +41,25 @@ func Test_networkServerErrorLogWriter_shouldDiscard(t *testing.T) { { name: "ipv4 trusted proxy (read)", proxies: []net.IP{net.ParseIP("10.24.0.32")}, - log: []byte("Sep 17 04:58:30 abydos incus.daemon[21884]: 2021/09/17 04:58:30 http: TLS handshake error from 10.24.0.32:55672: read tcp 10.24.0.22:8443->10.24.0.32:55672: read: connection reset by peer\n"), + log: []byte("Sep 17 04:58:30 abydos lxd.daemon[21884]: 2021/09/17 04:58:30 http: TLS handshake error from 10.24.0.32:55672: read tcp 10.24.0.22:8443->10.24.0.32:55672: read: connection reset by peer\n"), want: "", }, { name: "ipv4 non-trusted proxy (read)", proxies: []net.IP{net.ParseIP("10.24.0.33")}, - log: []byte("Sep 17 04:58:30 abydos incus.daemon[21884]: 2021/09/17 04:58:30 http: TLS handshake error from 10.24.0.32:55672: read tcp 10.24.0.22:8443->10.24.0.32:55672: read: connection reset by peer\n"), + log: []byte("Sep 17 04:58:30 abydos lxd.daemon[21884]: 2021/09/17 04:58:30 http: TLS handshake error from 10.24.0.32:55672: read tcp 10.24.0.22:8443->10.24.0.32:55672: read: connection reset by peer\n"), want: "http: TLS handshake error from 10.24.0.32:55672: read tcp 10.24.0.22:8443->10.24.0.32:55672: read: connection reset by peer", }, { name: "ipv6 trusted proxy (read)", proxies: []net.IP{net.ParseIP("2602:fd23:8:1003:216:3eff:fefa:7670")}, - log: []byte("Sep 17 04:58:30 abydos incus.daemon[21884]: 2021/09/17 04:58:30 http: TLS handshake error from [2602:fd23:8:1003:216:3eff:fefa:7670]:55672: read tcp [2602:fd23:8:101::100]:8443->[2602:fd23:8:1003:216:3eff:fefa:7670]:55672: read: connection reset by peer\n"), + log: []byte("Sep 17 04:58:30 abydos lxd.daemon[21884]: 2021/09/17 04:58:30 http: TLS handshake error from [2602:fd23:8:1003:216:3eff:fefa:7670]:55672: read tcp [2602:fd23:8:101::100]:8443->[2602:fd23:8:1003:216:3eff:fefa:7670]:55672: read: connection reset by peer\n"), want: "", }, { name: "ipv6 non-trusted proxy (read)", proxies: []net.IP{net.ParseIP("2602:fd23:8:1003:216:3eff:fefa:7671")}, - log: []byte("Sep 17 04:58:30 abydos incus.daemon[21884]: 2021/09/17 04:58:30 http: TLS handshake error from [2602:fd23:8:1003:216:3eff:fefa:7670]:55672: read tcp [2602:fd23:8:101::100]:8443->[2602:fd23:8:1003:216:3eff:fefa:7670]:55672: read: connection reset by peer\n"), + log: []byte("Sep 17 04:58:30 abydos lxd.daemon[21884]: 2021/09/17 04:58:30 http: TLS handshake error from [2602:fd23:8:1003:216:3eff:fefa:7670]:55672: read tcp [2602:fd23:8:101::100]:8443->[2602:fd23:8:1003:216:3eff:fefa:7670]:55672: read: connection reset by peer\n"), want: "http: TLS handshake error from [2602:fd23:8:1003:216:3eff:fefa:7670]:55672: read tcp [2602:fd23:8:101::100]:8443->[2602:fd23:8:1003:216:3eff:fefa:7670]:55672: read: connection reset by peer", }, diff --git a/lxd/instance/drivers/driver_common.go b/lxd/instance/drivers/driver_common.go index 7bca9f1070b8..9c1bb441adaf 100644 --- a/lxd/instance/drivers/driver_common.go +++ b/lxd/instance/drivers/driver_common.go @@ -509,8 +509,8 @@ func (d *common) deviceVolatileSetFunc(devName string) func(save map[string]stri // expandConfig applies the config of each profile in order, followed by the local config. func (d *common) expandConfig() error { - d.expandedConfig = db.ExpandInstanceConfig(d.localConfig, d.profiles) - d.expandedDevices = db.ExpandInstanceDevices(d.localDevices, d.profiles) + d.expandedConfig = instancetype.ExpandInstanceConfig(d.localConfig, d.profiles) + d.expandedDevices = instancetype.ExpandInstanceDevices(d.localDevices, d.profiles) return nil } diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index a54cff6ee9c9..e9d75fa7e2ef 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -823,12 +823,16 @@ func (*qemu) fwPath(filename string) string { func (d *qemu) killQemuProcess(pid int) error { proc, err := os.FindProcess(pid) if err != nil { + if err == os.ErrProcessDone { + return nil + } + return err } err = proc.Kill() if err != nil { - if strings.Contains(err.Error(), "process already finished") { + if err == os.ErrProcessDone { return nil } diff --git a/lxd/instance/drivers/driver_qemu_templates.go b/lxd/instance/drivers/driver_qemu_templates.go index 9ac9daca9126..57ea5909bea5 100644 --- a/lxd/instance/drivers/driver_qemu_templates.go +++ b/lxd/instance/drivers/driver_qemu_templates.go @@ -484,8 +484,16 @@ func qemuCPU(opts *qemuCPUOpts, pinning bool) []cfgSection { return nil } + // Cap the max number of CPUs to 64 unless directly assigned more. + max := 64 + if int(cpu.Total) < max { + max = int(cpu.Total) + } else if opts.cpuCount > max { + max = opts.cpuCount + } + entries = append(entries, cfgEntry{ - key: "maxcpus", value: fmt.Sprintf("%d", cpu.Total), + key: "maxcpus", value: fmt.Sprintf("%d", max), }) } diff --git a/lxd/instance/instancetype/instance_utils.go b/lxd/instance/instancetype/instance_utils.go new file mode 100644 index 000000000000..93f0ae03ecc1 --- /dev/null +++ b/lxd/instance/instancetype/instance_utils.go @@ -0,0 +1,54 @@ +package instancetype + +import ( + deviceConfig "github.com/canonical/lxd/lxd/device/config" + "github.com/canonical/lxd/shared/api" +) + +// ExpandInstanceConfig expands the given instance config with the config values of the given profiles. +func ExpandInstanceConfig(config map[string]string, profiles []api.Profile) map[string]string { + expandedConfig := map[string]string{} + + // Apply all the profiles. + profileConfigs := make([]map[string]string, len(profiles)) + for i, profile := range profiles { + profileConfigs[i] = profile.Config + } + + for i := range profileConfigs { + for k, v := range profileConfigs[i] { + expandedConfig[k] = v + } + } + + // Stick the given config on top. + for k, v := range config { + expandedConfig[k] = v + } + + return expandedConfig +} + +// ExpandInstanceDevices expands the given instance devices with the devices defined in the given profiles. +func ExpandInstanceDevices(devices deviceConfig.Devices, profiles []api.Profile) deviceConfig.Devices { + expandedDevices := deviceConfig.Devices{} + + // Apply all the profiles. + profileDevices := make([]deviceConfig.Devices, len(profiles)) + for i, profile := range profiles { + profileDevices[i] = deviceConfig.NewDevices(profile.Devices) + } + + for i := range profileDevices { + for k, v := range profileDevices[i] { + expandedDevices[k] = v + } + } + + // Stick the given devices on top. + for k, v := range devices { + expandedDevices[k] = v + } + + return expandedDevices +} diff --git a/lxd/instances_post.go b/lxd/instances_post.go index 8c0baa5866b4..3bff84c37054 100644 --- a/lxd/instances_post.go +++ b/lxd/instances_post.go @@ -755,7 +755,7 @@ func createFromBackup(s *state.State, r *http.Request, projectName string, data inst, err := instance.LoadByProjectAndName(s, bInfo.Project, bInfo.Name) if err != nil { - return fmt.Errorf("Load instance: %w", err) + return fmt.Errorf("Failed loading instance: %w", err) } // Clean up created instance if the post hook fails below. @@ -1134,8 +1134,8 @@ func instancesPost(d *Daemon, r *http.Request) response.Response { Reason: apiScriptlet.InstancePlacementReasonNew, } - reqExpanded.Config = db.ExpandInstanceConfig(reqExpanded.Config, profiles) - reqExpanded.Devices = db.ExpandInstanceDevices(deviceConfig.NewDevices(reqExpanded.Devices), profiles).CloneNative() + reqExpanded.Config = instancetype.ExpandInstanceConfig(reqExpanded.Config, profiles) + reqExpanded.Devices = instancetype.ExpandInstanceDevices(deviceConfig.NewDevices(reqExpanded.Devices), profiles).CloneNative() targetMemberInfo, err = scriptlet.InstancePlacementRun(r.Context(), logger.Log, s, &reqExpanded, candidateMembers, leaderAddress) if err != nil { diff --git a/lxd/network/acl/acl_load.go b/lxd/network/acl/acl_load.go index ac1339c37161..e92cde583c42 100644 --- a/lxd/network/acl/acl_load.go +++ b/lxd/network/acl/acl_load.go @@ -7,6 +7,7 @@ import ( "github.com/canonical/lxd/lxd/db" "github.com/canonical/lxd/lxd/db/cluster" deviceConfig "github.com/canonical/lxd/lxd/device/config" + "github.com/canonical/lxd/lxd/instance/instancetype" "github.com/canonical/lxd/lxd/project" "github.com/canonical/lxd/lxd/response" "github.com/canonical/lxd/lxd/state" @@ -243,7 +244,7 @@ func UsedBy(s *state.State, aclProjectName string, usageFunc func(ctx context.Co return nil } - devices := db.ExpandInstanceDevices(inst.Devices.Clone(), inst.Profiles) + devices := instancetype.ExpandInstanceDevices(inst.Devices.Clone(), inst.Profiles) // Iterate through each of the instance's devices, looking for NICs that are using any of the ACLs. for devName, devConfig := range devices { diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go index e7d192d994e8..73a0c33a2639 100644 --- a/lxd/network/driver_bridge.go +++ b/lxd/network/driver_bridge.go @@ -28,6 +28,7 @@ import ( "github.com/canonical/lxd/lxd/dnsmasq" "github.com/canonical/lxd/lxd/dnsmasq/dhcpalloc" firewallDrivers "github.com/canonical/lxd/lxd/firewall/drivers" + "github.com/canonical/lxd/lxd/instance/instancetype" "github.com/canonical/lxd/lxd/ip" "github.com/canonical/lxd/lxd/network/acl" "github.com/canonical/lxd/lxd/network/openvswitch" @@ -2482,7 +2483,7 @@ func (n *bridge) bridgedNICExternalRoutes(bridgeProjectNetworks map[string][]*ap return nil // Managed bridge networks can only exist in default project. } - devices := db.ExpandInstanceDevices(inst.Devices, inst.Profiles) + devices := instancetype.ExpandInstanceDevices(inst.Devices, inst.Profiles) // Iterate through each of the instance's devices, looking for bridged NICs that are linked to // networks specified. @@ -2582,7 +2583,7 @@ func (n *bridge) getExternalSubnetInUse() ([]externalSubnetUsage, error) { err = n.state.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error { // Detect if there are any conflicting proxy devices on all instances with the to be created network forward return tx.InstanceList(ctx, func(inst db.InstanceArgs, p api.Project) error { - devices := db.ExpandInstanceDevices(inst.Devices, inst.Profiles) + devices := instancetype.ExpandInstanceDevices(inst.Devices, inst.Profiles) for devName, devConfig := range devices { if devConfig["type"] != "proxy" { @@ -2756,7 +2757,7 @@ func (n *bridge) ForwardCreate(forward api.NetworkForwardsPost, clientType reque return nil // Managed bridge networks can only exist in default project. } - devices := db.ExpandInstanceDevices(inst.Devices.Clone(), inst.Profiles) + devices := instancetype.ExpandInstanceDevices(inst.Devices.Clone(), inst.Profiles) // Iterate through each of the instance's devices, looking for bridged NICs // that are linked to this network. diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index e61272021d64..4e1681904dd3 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -21,6 +21,7 @@ import ( dbCluster "github.com/canonical/lxd/lxd/db/cluster" deviceConfig "github.com/canonical/lxd/lxd/device/config" "github.com/canonical/lxd/lxd/instance" + "github.com/canonical/lxd/lxd/instance/instancetype" "github.com/canonical/lxd/lxd/ip" "github.com/canonical/lxd/lxd/locking" "github.com/canonical/lxd/lxd/network/acl" @@ -4303,7 +4304,7 @@ func (n *ovn) ovnNICExternalRoutes(ovnProjectNetworksWithOurUplink map[string][] return tx.InstanceList(ctx, func(inst db.InstanceArgs, p api.Project) error { // Get the instance's effective network project name. instNetworkProject := project.NetworkProjectFromRecord(&p) - devices := db.ExpandInstanceDevices(inst.Devices.Clone(), inst.Profiles) + devices := instancetype.ExpandInstanceDevices(inst.Devices.Clone(), inst.Profiles) // Iterate through each of the instance's devices, looking for OVN NICs that are linked to networks // that use our uplink. @@ -4430,7 +4431,7 @@ func (n *ovn) handleDependencyChange(uplinkName string, uplinkConfig map[string] return nil } - devices := db.ExpandInstanceDevices(inst.Devices.Clone(), inst.Profiles) + devices := instancetype.ExpandInstanceDevices(inst.Devices.Clone(), inst.Profiles) // Iterate through each of the instance's devices, looking for NICs that are linked // this network. diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go index 57e22be7bf83..2f0646ac0e76 100644 --- a/lxd/network/network_utils.go +++ b/lxd/network/network_utils.go @@ -109,7 +109,7 @@ func UsedByInstanceDevices(s *state.State, networkProjectName string, networkNam } // Look for NIC devices using this network. - devices := db.ExpandInstanceDevices(inst.Devices.Clone(), inst.Profiles) + devices := instancetype.ExpandInstanceDevices(inst.Devices.Clone(), inst.Profiles) for devName, devConfig := range devices { if isInUseByDevice(networkName, networkType, devConfig) { err := usageFunc(inst, devName, devConfig) diff --git a/lxd/network/network_utils_sriov.go b/lxd/network/network_utils_sriov.go index 75068a1ae6c9..4072643d79f9 100644 --- a/lxd/network/network_utils_sriov.go +++ b/lxd/network/network_utils_sriov.go @@ -16,6 +16,7 @@ import ( "github.com/canonical/lxd/lxd/db" dbCluster "github.com/canonical/lxd/lxd/db/cluster" "github.com/canonical/lxd/lxd/device/pci" + "github.com/canonical/lxd/lxd/instance/instancetype" "github.com/canonical/lxd/lxd/ip" "github.com/canonical/lxd/lxd/network/openvswitch" "github.com/canonical/lxd/lxd/state" @@ -61,8 +62,8 @@ func SRIOVGetHostDevicesInUse(s *state.State) (map[string]struct{}, error) { err = s.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error { return tx.InstanceList(ctx, func(dbInst db.InstanceArgs, p api.Project) error { // Expand configs so we take into account profile devices. - dbInst.Config = db.ExpandInstanceConfig(dbInst.Config, dbInst.Profiles) - dbInst.Devices = db.ExpandInstanceDevices(dbInst.Devices, dbInst.Profiles) + dbInst.Config = instancetype.ExpandInstanceConfig(dbInst.Config, dbInst.Profiles) + dbInst.Devices = instancetype.ExpandInstanceDevices(dbInst.Devices, dbInst.Profiles) for name, dev := range dbInst.Devices { // If device references a parent host interface name, mark that as reserved. diff --git a/lxd/project/permissions.go b/lxd/project/permissions.go index 18ef2f333479..fdf9a1a318f7 100644 --- a/lxd/project/permissions.go +++ b/lxd/project/permissions.go @@ -1244,8 +1244,8 @@ func expandInstancesConfigAndDevices(instances []api.Instance, profiles []api.Pr } expandedInstances[i] = instance - expandedInstances[i].Config = db.ExpandInstanceConfig(instance.Config, apiProfiles) - expandedInstances[i].Devices = db.ExpandInstanceDevices(deviceconfig.NewDevices(instance.Devices), apiProfiles).CloneNative() + expandedInstances[i].Config = instancetype.ExpandInstanceConfig(instance.Config, apiProfiles) + expandedInstances[i].Devices = instancetype.ExpandInstanceDevices(deviceconfig.NewDevices(instance.Devices), apiProfiles).CloneNative() } return expandedInstances, nil diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index 2af35a1c016a..aac41160fba8 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -882,6 +882,12 @@ func (b *lxdBackend) CreateInstanceFromBackup(srcBackup backup.Info, srcData io. return err } + // Save any changes that have occurred to the instance's config to the on-disk backup.yaml file. + err = b.UpdateInstanceBackupFile(inst, false, op) + if err != nil { + return fmt.Errorf("Failed updating backup file: %w", err) + } + // If the driver returned a post hook, run it now. if volPostHook != nil { // Initialise new volume containing root disk config supplied in instance. diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go index 74f144a34bd7..8537018c9933 100644 --- a/lxd/storage/utils.go +++ b/lxd/storage/utils.go @@ -1015,7 +1015,7 @@ func VolumeUsedByInstanceDevices(s *state.State, poolName string, projectName st // Expand devices for usage check if expandDevices is true. if expandDevices { - devices = db.ExpandInstanceDevices(devices.Clone(), inst.Profiles) + devices = instancetype.ExpandInstanceDevices(devices.Clone(), inst.Profiles) } var usedByDevices []string diff --git a/lxd/subprocess/proc.go b/lxd/subprocess/proc.go index a9fb05ee842c..db65a9111fb7 100644 --- a/lxd/subprocess/proc.go +++ b/lxd/subprocess/proc.go @@ -8,7 +8,6 @@ import ( "io" "os" "os/exec" - "strings" "syscall" "gopkg.in/yaml.v2" @@ -59,13 +58,25 @@ func (p *Process) hasApparmor() bool { // GetPid returns the pid for the given process object. func (p *Process) GetPid() (int64, error) { - pr, _ := os.FindProcess(int(p.PID)) - err := pr.Signal(syscall.Signal(0)) - if err == nil { - return p.PID, nil + pr, err := os.FindProcess(int(p.PID)) + if err != nil { + if err == os.ErrProcessDone { + return 0, ErrNotRunning + } + + return 0, err + } + + err = pr.Signal(syscall.Signal(0)) + if err != nil { + if err == os.ErrProcessDone { + return 0, ErrNotRunning + } + + return 0, err } - return 0, ErrNotRunning + return p.PID, nil } // SetApparmor allows setting the AppArmor profile. @@ -81,10 +92,21 @@ func (p *Process) SetCreds(uid uint32, gid uint32) { // Stop will stop the given process object. func (p *Process) Stop() error { - pr, _ := os.FindProcess(int(p.PID)) + pr, err := os.FindProcess(int(p.PID)) + if err != nil { + if err == os.ErrProcessDone { + if p.hasMonitor { + <-p.chExit + } + + return ErrNotRunning + } + + return err + } // Check if process exists. - err := pr.Signal(syscall.Signal(0)) + err = pr.Signal(syscall.Signal(0)) if err == nil { err = pr.Kill() if err == nil { @@ -97,7 +119,7 @@ func (p *Process) Stop() error { } // Check if either the existence check or the kill resulted in an already finished error. - if strings.Contains(err.Error(), "process already finished") { + if err == os.ErrProcessDone { if p.hasMonitor { <-p.chExit } @@ -212,8 +234,16 @@ func (p *Process) Restart(ctx context.Context) error { // Reload sends the SIGHUP signal to the given process object. func (p *Process) Reload() error { - pr, _ := os.FindProcess(int(p.PID)) - err := pr.Signal(syscall.Signal(0)) + pr, err := os.FindProcess(int(p.PID)) + if err != nil { + if err == os.ErrProcessDone { + return ErrNotRunning + } + + return fmt.Errorf("Could not reload process: %w", err) + } + + err = pr.Signal(syscall.Signal(0)) if err == nil { err = pr.Signal(syscall.SIGHUP) if err != nil { @@ -221,7 +251,7 @@ func (p *Process) Reload() error { } return nil - } else if strings.Contains(err.Error(), "process already finished") { + } else if err == os.ErrProcessDone { return ErrNotRunning } @@ -245,8 +275,16 @@ func (p *Process) Save(path string) error { // Signal will send a signal to the given process object given a signal value. func (p *Process) Signal(signal int64) error { - pr, _ := os.FindProcess(int(p.PID)) - err := pr.Signal(syscall.Signal(0)) + pr, err := os.FindProcess(int(p.PID)) + if err != nil { + if err == os.ErrProcessDone { + return ErrNotRunning + } + + return err + } + + err = pr.Signal(syscall.Signal(0)) if err == nil { err = pr.Signal(syscall.Signal(signal)) if err != nil { @@ -254,7 +292,7 @@ func (p *Process) Signal(signal int64) error { } return nil - } else if strings.Contains(err.Error(), "process already finished") { + } else if err == os.ErrProcessDone { return ErrNotRunning }