The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6848
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === When the lxc client's terminal exits, ensure we don't leak go routines on the LXD server and keep lxc client processes around by ensuring that the the websocket mirror is cleaned up.
From bd28effdb629aafc1a667b5980fd6f13b583eeea Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 4 Feb 2020 09:53:11 +0000 Subject: [PATCH 01/19] lxd/storage/drivers/driver/lvm: Uses d.thinpoolName() rather than d.config["lvm.thinpool_name"] During thin pool rename requests, use d.thinpoolName() rather than d.config["lvm.thinpool_name"] in case d.config["lvm.thinpool_name"] is not defined. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_lvm.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/storage/drivers/driver_lvm.go b/lxd/storage/drivers/driver_lvm.go index 04ccc4725c..09423c1ba1 100644 --- a/lxd/storage/drivers/driver_lvm.go +++ b/lxd/storage/drivers/driver_lvm.go @@ -466,11 +466,11 @@ func (d *lvm) Update(changedConfig map[string]string) error { } if changedConfig["lvm.thinpool_name"] != "" { - _, err := shared.TryRunCommand("lvrename", d.config["lvm.vg_name"], d.config["lvm.thinpool_name"], changedConfig["lvm.thinpool_name"]) + _, err := shared.TryRunCommand("lvrename", d.config["lvm.vg_name"], d.thinpoolName(), changedConfig["lvm.thinpool_name"]) if err != nil { - return errors.Wrapf(err, "Error renaming LVM thin pool from %q to %q", d.config["lvm.thinpool_name"], changedConfig["lvm.thinpool_name"]) + return errors.Wrapf(err, "Error renaming LVM thin pool from %q to %q", d.thinpoolName(), changedConfig["lvm.thinpool_name"]) } - d.logger.Debug("Thin pool volume renamed", log.Ctx{"vg_name": d.config["lvm.vg_name"], "thinpool": d.config["lvm.thinpool_name"], "new_thinpool": changedConfig["lvm.thinpool_name"]}) + d.logger.Debug("Thin pool volume renamed", log.Ctx{"vg_name": d.config["lvm.vg_name"], "thinpool": d.thinpoolName(), "new_thinpool": changedConfig["lvm.thinpool_name"]}) } return nil From 4d97547265cb124b72e1a92aece3eb7da853020a Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 5 Feb 2020 09:55:47 +0000 Subject: [PATCH 02/19] lxd/patches: setupStorageDriver usage Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/patches.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/patches.go b/lxd/patches.go index 4ff0e23157..8fe0db7509 100644 --- a/lxd/patches.go +++ b/lxd/patches.go @@ -451,7 +451,7 @@ func patchStorageApi(name string, d *Daemon) error { return err } - return SetupStorageDriver(d.State(), true) + return setupStorageDriver(d.State(), true) } func upgradeFromStorageTypeBtrfs(name string, d *Daemon, defaultPoolName string, defaultStorageTypeName string, cRegular []string, cSnapshots []string, imgPublic []string, imgPrivate []string) error { From 816d3f875714a495c91ab1cde6a36e2885ac1077 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 5 Feb 2020 09:58:10 +0000 Subject: [PATCH 03/19] lxd/storage: Renames SetupStorageDriver to setupStorageDriver for consistency Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage.go b/lxd/storage.go index 38d30a404d..71d5b02a26 100644 --- a/lxd/storage.go +++ b/lxd/storage.go @@ -711,7 +711,7 @@ func resetContainerDiskIdmap(container *containerLXC, srcIdmap *idmap.IdmapSet) return nil } -func SetupStorageDriver(s *state.State, forceCheck bool) error { +func setupStorageDriver(s *state.State, forceCheck bool) error { pools, err := s.Cluster.StoragePoolsNotPending() if err != nil { if err == db.ErrNoSuchObject { From 5f2468b5cb72e07ce2c689db1d81ebd5026e2217 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 5 Feb 2020 10:02:09 +0000 Subject: [PATCH 04/19] lxd/storage/drivers/driver/zfs: Adds zfs kernel module load fail detection Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_zfs.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lxd/storage/drivers/driver_zfs.go b/lxd/storage/drivers/driver_zfs.go index 4071ec1a26..8b390a835e 100644 --- a/lxd/storage/drivers/driver_zfs.go +++ b/lxd/storage/drivers/driver_zfs.go @@ -50,7 +50,10 @@ func (d *zfs) load() error { } // Load the kernel module. - util.LoadModule("zfs") + err := util.LoadModule("zfs") + if err != nil { + return errors.Wrapf(err, "Error loading %q module", "zfs") + } // Validate the needed tools are present. for _, tool := range []string{"zpool", "zfs"} { From 7deb9534fd3d4c50a968cdb659ec8c52c19352be Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 09:05:20 +0000 Subject: [PATCH 05/19] lxd/daemon: setupStorageDriver usage Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/daemon.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/daemon.go b/lxd/daemon.go index fd0e4b57fc..77b074220a 100644 --- a/lxd/daemon.go +++ b/lxd/daemon.go @@ -815,7 +815,7 @@ func (d *Daemon) init() error { /* Read the storage pools */ logger.Infof("Initializing storage pools") - err = SetupStorageDriver(d.State(), false) + err = setupStorageDriver(d.State(), false) if err != nil { return err } From 5009b2fc77ef0a39b3f895f3d117a4b62a7c9ae0 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 09:05:33 +0000 Subject: [PATCH 06/19] lxd/daemon: Comment consistency Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/daemon.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lxd/daemon.go b/lxd/daemon.go index 77b074220a..6bdb4958b7 100644 --- a/lxd/daemon.go +++ b/lxd/daemon.go @@ -808,41 +808,41 @@ func (d *Daemon) init() error { containersRestart(s) } - // Setup the user-agent + // Setup the user-agent. if clustered { version.UserAgentFeatures([]string{"cluster"}) } - /* Read the storage pools */ + // Read the storage pools. logger.Infof("Initializing storage pools") err = setupStorageDriver(d.State(), false) if err != nil { return err } - // Mount any daemon storage + // Mount any daemon storage. err = daemonStorageMount(d.State()) if err != nil { return err } - /* Apply all patches */ + // Apply all patches. err = patchesApplyAll(d) if err != nil { return err } - /* Setup the networks */ + // Setup the networks. logger.Infof("Initializing networks") err = networkStartup(d.State()) if err != nil { return err } - // Cleanup leftover images + // Cleanup leftover images. pruneLeftoverImages(d) - /* Setup the proxy handler, external authentication and MAAS */ + // Setup the proxy handler, external authentication and MAAS. candidAPIURL := "" candidAPIKey := "" candidDomains := "" From d01d09942dfcb8e20e2dd3796f8b4789eadf54e6 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 09:15:17 +0000 Subject: [PATCH 07/19] lxd/storage/drivers/driver/lvm: Makes lvm.vg_name required for mounting Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_lvm.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lxd/storage/drivers/driver_lvm.go b/lxd/storage/drivers/driver_lvm.go index 09423c1ba1..34a3294aee 100644 --- a/lxd/storage/drivers/driver_lvm.go +++ b/lxd/storage/drivers/driver_lvm.go @@ -479,6 +479,10 @@ func (d *lvm) Update(changedConfig map[string]string) error { // Mount mounts the storage pool (this does nothing for external LVM pools, but for loopback image // LVM pools this creates a loop device). func (d *lvm) Mount() (bool, error) { + if d.config["lvm.vg_name"] == "" { + return false, fmt.Errorf("Cannot mount pool as %q is not specified", "lvm.vg_name") + } + // Open the loop file if the LVM device doesn't exist yet and the source points to a file. if !shared.IsDir(fmt.Sprintf("/dev/%s", d.config["lvm.vg_name"])) && filepath.IsAbs(d.config["source"]) && !shared.IsBlockdevPath(d.config["source"]) { loopFile, err := d.openLoopFile(d.config["source"]) From 112be17b0df238d4919a07f8fbb4d21191b0eb89 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 09:15:43 +0000 Subject: [PATCH 08/19] lxd/db/cluster/update: Adds updateFromV23 for ensuring lvm.vg_name key is set Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/db/cluster/update.go | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/lxd/db/cluster/update.go b/lxd/db/cluster/update.go index 1466f9f13e..6b3d353a9a 100644 --- a/lxd/db/cluster/update.go +++ b/lxd/db/cluster/update.go @@ -59,6 +59,49 @@ var updates = map[int]schema.Update{ 21: updateFromV20, 22: updateFromV21, 23: updateFromV22, + 24: updateFromV23, +} + +// The lvm.vg_name config key is required for LVM to function. +func updateFromV23(tx *sql.Tx) error { + // Fetch the IDs of all existing nodes. + nodeIDs, err := query.SelectIntegers(tx, "SELECT id FROM nodes") + if err != nil { + return errors.Wrap(err, "Failed to get IDs of current nodes") + } + + // Fetch the IDs of all existing lvm pools. + poolIDs, err := query.SelectIntegers(tx, `SELECT id FROM storage_pools WHERE driver='lvm'`) + if err != nil { + return errors.Wrap(err, "Failed to get IDs of current lvm pools") + } + + for _, poolID := range poolIDs { + for _, nodeID := range nodeIDs { + // Fetch the config for this lvm pool. + config, err := query.SelectConfig(tx, "storage_pools_config", "storage_pool_id=? AND node_id=?", poolID, nodeID) + if err != nil { + return errors.Wrap(err, "Failed to fetch of lvm pool config") + } + + // Check if already set. + _, ok := config["lvm.vg_name"] + if ok { + continue + } + + // Add lvm.vg_name config entry. + _, err = tx.Exec(` +INSERT INTO storage_pools_config(storage_pool_id, node_id, key, value) +SELECT ?, ?, 'lvm.vg_name', name FROM storage_pools WHERE id=? +`, poolID, nodeID, poolID) + if err != nil { + return errors.Wrap(err, "Failed to create lvm.vg_name node config") + } + } + } + + return nil } // The zfs.pool_name config key is required for ZFS to function. From 2463ccea11ff21c8b2bb9d02a3750eda6c364840 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 09:16:03 +0000 Subject: [PATCH 09/19] lxd/db/cluster/update: Superfluous trailing whitespace Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/db/cluster/update.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/db/cluster/update.go b/lxd/db/cluster/update.go index 6b3d353a9a..7b9763a238 100644 --- a/lxd/db/cluster/update.go +++ b/lxd/db/cluster/update.go @@ -174,12 +174,12 @@ CREATE TABLE images_profiles ( FOREIGN KEY (profile_id) REFERENCES profiles (id) ON DELETE CASCADE, UNIQUE (image_id, profile_id) ); -INSERT INTO images_profiles (image_id, profile_id) +INSERT INTO images_profiles (image_id, profile_id) SELECT images.id, profiles.id FROM images JOIN profiles ON images.project_id = profiles.project_id WHERE profiles.name = 'default'; INSERT INTO images_profiles (image_id, profile_id) - SELECT images.id, profiles.id FROM projects_config AS R + SELECT images.id, profiles.id FROM projects_config AS R JOIN projects_config AS S ON R.project_id = S.project_id JOIN images ON images.project_id = R.project_id JOIN profiles ON profiles.project_id = 1 AND profiles.name = "default" From d10dd39459732167a4f542430e1dd629b2108877 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 09:17:23 +0000 Subject: [PATCH 10/19] lxd/db/cluster/schema: v24 update Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/db/cluster/schema.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/db/cluster/schema.go b/lxd/db/cluster/schema.go index a7bd5e2b05..02f2057d10 100644 --- a/lxd/db/cluster/schema.go +++ b/lxd/db/cluster/schema.go @@ -495,5 +495,5 @@ CREATE TABLE storage_volumes_config ( FOREIGN KEY (storage_volume_id) REFERENCES storage_volumes (id) ON DELETE CASCADE ); -INSERT INTO schema (version, updated_at) VALUES (23, strftime("%s")) +INSERT INTO schema (version, updated_at) VALUES (24, strftime("%s")) ` From ecde4cc3a3b1da1471e38c934d837d9779c20162 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 10:19:50 +0000 Subject: [PATCH 11/19] lxd/device/config/devices: Adds NICType function on Device type This function centralises the logic used to derive the nictype property from both the network and nictype properties. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/device/config/devices.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lxd/device/config/devices.go b/lxd/device/config/devices.go index f6ec31c1e4..d574e19adc 100644 --- a/lxd/device/config/devices.go +++ b/lxd/device/config/devices.go @@ -19,6 +19,21 @@ func (device Device) Clone() Device { return copy } +// NICType returns the derived NIC Type for a NIC device. +// If the "network" property is specified then this implicitly (at least for now) means the nictype is "bridged". +// Otherwise the "nictype" property is returned. If the device type is not a NIC then an empty string is returned. +func (device Device) NICType() string { + if device["type"] == "nic" { + if device["network"] != "" { + return "bridged" + } + + return device["nictype"] + } + + return "" +} + // Validate accepts a map of field/validation functions to run against the device's config. func (device Device) Validate(rules map[string]func(value string) error) error { checkedFields := map[string]struct{}{} From bd6aca8614e1634a80b0aa1ebea5502cfffa8141 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 10:21:16 +0000 Subject: [PATCH 12/19] lxd: Device.NICType usage Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/container_lxc.go | 6 +++--- lxd/device/device_utils_network.go | 4 ++-- lxd/device/infiniband.go | 2 +- lxd/device/nic.go | 2 +- lxd/device/proxy.go | 2 +- lxd/instance/drivers/driver_qemu.go | 8 ++++---- lxd/network/network_utils.go | 4 ++-- lxd/networks.go | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go index f958f87ffc..5f2f972754 100644 --- a/lxd/container_lxc.go +++ b/lxd/container_lxc.go @@ -1724,7 +1724,7 @@ func (c *containerLXC) deviceResetVolatile(devName string, oldConfig, newConfig // If the device type has changed, remove all old volatile keys. // This will occur if the newConfig is empty (i.e the device is actually being removed) or // if the device type is being changed but keeping the same name. - if newConfig["type"] != oldConfig["type"] || newConfig["nictype"] != oldConfig["nictype"] { + if newConfig["type"] != oldConfig["type"] || newConfig.NICType() != oldConfig.NICType() { for k := range c.localConfig { if !strings.HasPrefix(k, devicePrefix) { continue @@ -4094,7 +4094,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error { // between oldDevice and newDevice. The result of this is that as long as the // devices are otherwise identical except for the fields returned here, then the // device is considered to be being "updated" rather than "added & removed". - if oldDevice["type"] != newDevice["type"] || oldDevice["nictype"] != newDevice["nictype"] { + if oldDevice["type"] != newDevice["type"] || oldDevice.NICType() != newDevice.NICType() { return []string{} // Device types aren't the same, so this cannot be an update. } @@ -6443,7 +6443,7 @@ func (c *containerLXC) FillNetworkDevice(name string, m deviceConfig.Device) (de } // Fill in the MAC address - if !shared.StringInSlice(m["nictype"], []string{"physical", "ipvlan", "sriov"}) && m["hwaddr"] == "" { + if !shared.StringInSlice(m.NICType(), []string{"physical", "ipvlan", "sriov"}) && m["hwaddr"] == "" { configKey := fmt.Sprintf("volatile.%s.hwaddr", name) volatileHwaddr := c.localConfig[configKey] if volatileHwaddr == "" { diff --git a/lxd/device/device_utils_network.go b/lxd/device/device_utils_network.go index 4c9c5e757d..a8850aa5c8 100644 --- a/lxd/device/device_utils_network.go +++ b/lxd/device/device_utils_network.go @@ -388,7 +388,7 @@ func networkSetupHostVethDevice(device deviceConfig.Device, oldDevice deviceConf func networkSetVethRoutes(m deviceConfig.Device) error { // Decide whether the route should point to the veth parent or the bridge parent. routeDev := m["host_name"] - if m["nictype"] == "bridged" { + if m.NICType() == "bridged" { routeDev = m["parent"] } @@ -426,7 +426,7 @@ func networkSetVethRoutes(m deviceConfig.Device) error { func networkRemoveVethRoutes(m deviceConfig.Device) { // Decide whether the route should point to the veth parent or the bridge parent routeDev := m["host_name"] - if m["nictype"] == "bridged" { + if m.NICType() == "bridged" { routeDev = m["parent"] } diff --git a/lxd/device/infiniband.go b/lxd/device/infiniband.go index 9fc586d941..adcb411c87 100644 --- a/lxd/device/infiniband.go +++ b/lxd/device/infiniband.go @@ -12,7 +12,7 @@ var infinibandTypes = map[string]func() device{ // infinibandLoadByType returns an Infiniband device instantiated with supplied config. func infinibandLoadByType(c deviceConfig.Device) device { - f := infinibandTypes[c["nictype"]] + f := infinibandTypes[c.NICType()] if f != nil { return f() } diff --git a/lxd/device/nic.go b/lxd/device/nic.go index f8b5bd3522..d33a8820a3 100644 --- a/lxd/device/nic.go +++ b/lxd/device/nic.go @@ -18,7 +18,7 @@ var nicTypes = map[string]func() device{ // nicLoadByType returns a NIC device instantiated with supplied config. func nicLoadByType(c deviceConfig.Device) device { - f := nicTypes[c["nictype"]] + f := nicTypes[c.NICType()] if f != nil { return f() } diff --git a/lxd/device/proxy.go b/lxd/device/proxy.go index 77a1394575..3e6f38b558 100644 --- a/lxd/device/proxy.go +++ b/lxd/device/proxy.go @@ -267,7 +267,7 @@ func (d *proxy) setupNAT() error { var IPv6Addr net.IP for _, devConfig := range d.inst.ExpandedDevices() { - if devConfig["type"] != "nic" || (devConfig["type"] == "nic" && devConfig["nictype"] != "bridged") { + if devConfig["type"] != "nic" || (devConfig["type"] == "nic" && devConfig.NICType() != "bridged") { continue } diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index d85468be2b..efb2276fbb 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -2160,7 +2160,7 @@ func (vm *qemu) Update(args db.InstanceArgs, userRequested bool) error { // between oldDevice and newDevice. The result of this is that as long as the // devices are otherwise identical except for the fields returned here, then the // device is considered to be being "updated" rather than "added & removed". - if oldDevice["type"] != newDevice["type"] || oldDevice["nictype"] != newDevice["nictype"] { + if oldDevice["type"] != newDevice["type"] || oldDevice.NICType() != newDevice.NICType() { return []string{} // Device types aren't the same, so this cannot be an update. } @@ -2371,7 +2371,7 @@ func (vm *qemu) deviceResetVolatile(devName string, oldConfig, newConfig deviceC // If the device type has changed, remove all old volatile keys. // This will occur if the newConfig is empty (i.e the device is actually being removed) or // if the device type is being changed but keeping the same name. - if newConfig["type"] != oldConfig["type"] || newConfig["nictype"] != oldConfig["nictype"] { + if newConfig["type"] != oldConfig["type"] || newConfig.NICType() != oldConfig.NICType() { for k := range vm.localConfig { if !strings.HasPrefix(k, devicePrefix) { continue @@ -3079,7 +3079,7 @@ func (vm *qemu) RenderState() (*api.InstanceState, error) { networks := map[string]api.InstanceStateNetwork{} for k, m := range vm.ExpandedDevices() { // We only care about nics. - if m["type"] != "nic" || m["nictype"] != "bridged" { + if m["type"] != "nic" || m.NICType() != "bridged" { continue } @@ -3451,7 +3451,7 @@ func (vm *qemu) FillNetworkDevice(name string, m deviceConfig.Device) (deviceCon } // Fill in the MAC address - if !shared.StringInSlice(m["nictype"], []string{"physical", "ipvlan", "sriov"}) && m["hwaddr"] == "" { + if !shared.StringInSlice(m.NICType(), []string{"physical", "ipvlan", "sriov"}) && m["hwaddr"] == "" { configKey := fmt.Sprintf("volatile.%s.hwaddr", name) volatileHwaddr := vm.localConfig[configKey] if volatileHwaddr == "" { diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go index 176abbfc8e..d911cbdf1b 100644 --- a/lxd/network/network_utils.go +++ b/lxd/network/network_utils.go @@ -34,7 +34,7 @@ func IsInUse(c instance.Instance, networkName string) bool { continue } - if !shared.StringInSlice(d["nictype"], []string{"bridged", "macvlan", "ipvlan", "physical", "sriov"}) { + if !shared.StringInSlice(d.NICType(), []string{"bridged", "macvlan", "ipvlan", "physical", "sriov"}) { continue } @@ -236,7 +236,7 @@ func UpdateDNSMasqStatic(s *state.State, networkName string) error { // Go through all its devices (including profiles for k, d := range inst.ExpandedDevices() { // Skip uninteresting entries - if d["type"] != "nic" || d["nictype"] != "bridged" || !shared.StringInSlice(d["parent"], networks) { + if d["type"] != "nic" || d.NICType() != "bridged" || !shared.StringInSlice(d["parent"], networks) { continue } diff --git a/lxd/networks.go b/lxd/networks.go index 4c71abd70c..44063b1a5f 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -699,7 +699,7 @@ func networkLeasesGet(d *Daemon, r *http.Request) response.Response { // Go through all its devices (including profiles for k, d := range inst.ExpandedDevices() { // Skip uninteresting entries - if d["type"] != "nic" || d["nictype"] != "bridged" || d["parent"] != name { + if d["type"] != "nic" || d.NICType() != "bridged" || d["parent"] != name { continue } From e4f521ac535d10eec7f8b80d56a294547afc0f12 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 10:22:36 +0000 Subject: [PATCH 13/19] lxd/device/nic/bridged: Bans use of nictype when using network property Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/device/nic_bridged.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go index af982f0a87..ad089ae747 100644 --- a/lxd/device/nic_bridged.go +++ b/lxd/device/nic_bridged.go @@ -76,7 +76,7 @@ func (d *nicBridged) validateConfig(instConf instance.ConfigReader) error { if d.config["network"] != "" { requiredFields = append(requiredFields, "network") - bannedKeys := []string{"parent", "mtu", "maas.subnet.ipv4", "maas.subnet.ipv6"} + bannedKeys := []string{"nictype", "parent", "mtu", "maas.subnet.ipv4", "maas.subnet.ipv6"} for _, bannedKey := range bannedKeys { if d.config[bannedKey] != "" { return fmt.Errorf("Cannot use %q property in conjunction with %q property", bannedKey, "network") From 0af1c3869d6d8ea3e2fde64df462774ca5f27f15 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 10:22:59 +0000 Subject: [PATCH 14/19] test: Updates nic bridged tests for NICType logic Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- test/suites/container_devices_nic_bridged.sh | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test/suites/container_devices_nic_bridged.sh b/test/suites/container_devices_nic_bridged.sh index f78a7a7959..dbde3897dd 100644 --- a/test/suites/container_devices_nic_bridged.sh +++ b/test/suites/container_devices_nic_bridged.sh @@ -446,13 +446,14 @@ test_container_devices_nic_bridged() { # Can't use network property when parent is set. ! lxc profile device set "${ctName}" eth0 network="${brName}" - # Reset mtu and parent settings and assign network in one command. - lxc profile device set "${ctName}" eth0 mtu="" parent="" network="${brName}" + # Remove mtu, nictype and parent settings and assign network in one command. + lxc profile device set "${ctName}" eth0 mtu="" parent="" nictype="" network="${brName}" # Can't remove network if parent not specified. ! lxc profile device unset "${ctName}" eth0 network # Can't use some settings when network is set. + ! lxc profile device set "${ctName}" eth0 nictype="bridged" ! lxc profile device set "${ctName}" eth0 mtu="1400" ! lxc profile device set "${ctName}" eth0 maas.subnet.ipv4="test" ! lxc profile device set "${ctName}" eth0 maas.subnet.ipv6="test" @@ -469,6 +470,17 @@ test_container_devices_nic_bridged() { echo "container mtu has not been inherited from network" false fi + + # Check profile routes are applied on boot (as these use derived nictype). + if ! ip -4 r list dev "${brName}" | grep "192.0.2.1${ipRand}" ; then + echo "ipv4.routes invalid" + false + fi + if ! ip -6 r list dev "${brName}" | grep "2001:db8::1${ipRand}" ; then + echo "ipv6.routes invalid" + false + fi + lxc stop -f "${ctName}" lxc network unset "${brName}" bridge.mtu From 2360fa7f0ddd38b65c1581048c396a4552b39890 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 10:51:44 +0000 Subject: [PATCH 15/19] lxd/network/network/utils: Fix network in use detection Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/network/network_utils.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go index d911cbdf1b..4499bc8ed9 100644 --- a/lxd/network/network_utils.go +++ b/lxd/network/network_utils.go @@ -38,11 +38,15 @@ func IsInUse(c instance.Instance, networkName string) bool { continue } + if d["network"] == networkName { + return true + } + if d["parent"] == "" { continue } - if GetHostDevice(d["parent"], d["vlan"]) == networkName || d["network"] == networkName { + if GetHostDevice(d["parent"], d["vlan"]) == networkName { return true } } From 08c173228a85a5f788dec1bdd26d8a722be56c67 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 11:10:34 +0000 Subject: [PATCH 16/19] lxd-agent/exec: Logs signal forwarding as info rather than error It isn't an error. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd-agent/exec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd-agent/exec.go b/lxd-agent/exec.go index 2908882d2f..1feddb8325 100644 --- a/lxd-agent/exec.go +++ b/lxd-agent/exec.go @@ -339,7 +339,7 @@ func (s *execWs) Do(op *operations.Operation) error { logger.Errorf("Failed forwarding signal '%d' to PID %d", command.Signal, attachedChildPid) continue } - logger.Errorf("Forwarded signal '%d' to PID %d", command.Signal, attachedChildPid) + logger.Infof("Forwarded signal '%d' to PID %d", command.Signal, attachedChildPid) } } }() From ee18203d66c821567c8c83f483068143560ad679 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 12:15:30 +0000 Subject: [PATCH 17/19] lxd/container/exec: Only log finished mirror websocket when go routine exits Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/container_exec.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lxd/container_exec.go b/lxd/container_exec.go index c35fab967f..2302ee54b3 100644 --- a/lxd/container_exec.go +++ b/lxd/container_exec.go @@ -241,12 +241,11 @@ func (s *execWs) Do(op *operations.Operation) error { s.connsLock.Unlock() logger.Debugf("Started mirroring websocket") + defer logger.Debugf("Finished mirroring websocket") readDone, writeDone := netutils.WebsocketExecMirror(conn, ptys[0], ptys[0], attachedChildIsDead, int(ptys[0].Fd())) <-readDone <-writeDone - logger.Debugf("Finished mirroring websocket") - conn.Close() wgEOF.Done() }() From 10f9cb93206c82cbe825b58db720eda218c5d59d Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 12:15:51 +0000 Subject: [PATCH 18/19] lxd/instance/drivers/driver/qemu: Fix go routine leak and hanging lxc clients Ensure that websocket mirror process ends when client terminal exits. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/instance/drivers/driver_qemu.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index efb2276fbb..83a7522315 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -2904,7 +2904,14 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin *os.File, stdout *os.File, return nil, err } - revert.Add(func() { termios.Restore(int(stdin.Fd()), oldttystate) }) + revert.Add(func() { + termios.Restore(int(stdin.Fd()), oldttystate) + + // Write a reset escape sequence to the console to cancel any ongoing reads to the handle + // and then close it. + stdout.Write([]byte("\x1bc")) + stdout.Close() + }) } dataDone := make(chan bool) From 41bb1e879fef6f8682287df14cee6ee13f034454 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 6 Feb 2020 12:16:42 +0000 Subject: [PATCH 19/19] shared: Upper case first character of some debug messages Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- shared/netutils/network_linux.go | 2 +- shared/network.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shared/netutils/network_linux.go b/shared/netutils/network_linux.go index f634aac8ed..cbea0423b3 100644 --- a/shared/netutils/network_linux.go +++ b/shared/netutils/network_linux.go @@ -183,7 +183,7 @@ func WebsocketExecMirror(conn *websocket.Conn, w io.WriteCloser, r io.ReadCloser buf, ok := <-in if !ok { r.Close() - logger.Debugf("sending write barrier") + logger.Debugf("Sending write barrier") err := conn.WriteMessage(websocket.TextMessage, []byte{}) if err != nil { logger.Debugf("Got err writing barrier %s", err) diff --git a/shared/network.go b/shared/network.go index f8604d2232..71e5f09551 100644 --- a/shared/network.go +++ b/shared/network.go @@ -206,7 +206,7 @@ func WebsocketRecvStream(w io.Writer, conn *websocket.Conn) chan bool { } if mt == websocket.TextMessage { - logger.Debugf("got message barrier") + logger.Debugf("Got message barrier") break } @@ -296,7 +296,7 @@ func defaultReader(conn *websocket.Conn, r io.ReadCloser, readDone chan<- bool) buf, ok := <-in if !ok { r.Close() - logger.Debugf("sending write barrier") + logger.Debugf("Sending write barrier") conn.WriteMessage(websocket.TextMessage, []byte{}) readDone <- true return @@ -460,7 +460,7 @@ func WebsocketConsoleMirror(conn *websocket.Conn, w io.WriteCloser, r io.ReadClo buf, ok := <-in if !ok { r.Close() - logger.Debugf("sending write barrier") + logger.Debugf("Sending write barrier") conn.WriteMessage(websocket.BinaryMessage, []byte("\r")) conn.WriteMessage(websocket.TextMessage, []byte{}) readDone <- true
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel