diff --git a/internal/server/device/nic_bridged.go b/internal/server/device/nic_bridged.go index c72308dc7b9..5b47329c09a 100644 --- a/internal/server/device/nic_bridged.go +++ b/internal/server/device/nic_bridged.go @@ -813,6 +813,12 @@ func (d *nicBridged) Stop() (*deviceConfig.RunConfig, error) { // postStop is run after the device is removed from the instance. func (d *nicBridged) postStop() error { + // Handle the case where validation fails but the device still must be removed. + bridgeName := d.config["parent"] + if bridgeName == "" && d.config["network"] != "" { + bridgeName = d.config["network"] + } + defer func() { _ = d.volatileSet(map[string]string{ "host_name": "", @@ -825,9 +831,9 @@ func (d *nicBridged) postStop() error { if d.config["host_name"] != "" && network.InterfaceExists(d.config["host_name"]) { // Detach host-side end of veth pair from bridge (required for openvswitch particularly). - err := network.DetachInterface(d.state, d.config["parent"], d.config["host_name"]) + err := network.DetachInterface(d.state, bridgeName, d.config["host_name"]) if err != nil { - return fmt.Errorf("Failed to detach interface %q from %q: %w", d.config["host_name"], d.config["parent"], err) + return fmt.Errorf("Failed to detach interface %q from %q: %w", d.config["host_name"], bridgeName, err) } // Removing host-side end of veth pair will delete the peer end too. @@ -843,7 +849,7 @@ func (d *nicBridged) postStop() error { routes = append(routes, util.SplitNTrimSpace(d.config["ipv6.routes"], ",", -1, true)...) routes = append(routes, util.SplitNTrimSpace(d.config["ipv4.routes.external"], ",", -1, true)...) routes = append(routes, util.SplitNTrimSpace(d.config["ipv6.routes.external"], ",", -1, true)...) - networkNICRouteDelete(d.config["parent"], routes...) + networkNICRouteDelete(bridgeName, routes...) if util.IsTrue(d.config["security.mac_filtering"]) || util.IsTrue(d.config["security.ipv4_filtering"]) || util.IsTrue(d.config["security.ipv6_filtering"]) { d.removeFilters(d.config) @@ -854,25 +860,31 @@ func (d *nicBridged) postStop() error { // Remove is run when the device is removed from the instance or the instance is deleted. func (d *nicBridged) Remove() error { - if d.config["parent"] != "" { + // Handle the case where validation fails but the device still must be removed. + bridgeName := d.config["parent"] + if bridgeName == "" && d.config["network"] != "" { + bridgeName = d.config["network"] + } + + if bridgeName != "" { dnsmasq.ConfigMutex.Lock() defer dnsmasq.ConfigMutex.Unlock() - if network.InterfaceExists(d.config["parent"]) { - err := d.networkClearLease(d.inst.Name(), d.config["parent"], d.config["hwaddr"], clearLeaseAll) + if network.InterfaceExists(bridgeName) { + err := d.networkClearLease(d.inst.Name(), bridgeName, d.config["hwaddr"], clearLeaseAll) if err != nil { return fmt.Errorf("Failed clearing leases: %w", err) } } // Remove dnsmasq config if it exists (doesn't return error if file is missing). - err := dnsmasq.RemoveStaticEntry(d.config["parent"], d.inst.Project().Name, d.inst.Name(), d.Name()) + err := dnsmasq.RemoveStaticEntry(bridgeName, d.inst.Project().Name, d.inst.Name(), d.Name()) if err != nil { return err } // Reload dnsmasq to apply new settings if dnsmasq is running. - err = dnsmasq.Kill(d.config["parent"], true) + err = dnsmasq.Kill(bridgeName, true) if err != nil { return err }