From lxc-bot at linuxcontainers.org Thu Oct 1 04:27:22 2020 From: lxc-bot at linuxcontainers.org (Saurabh299 on Github) Date: Wed, 30 Sep 2020 21:27:22 -0700 (PDT) Subject: [lxc-devel] [linuxcontainers.org/master] improved commons public license Message-ID: <5f755aaa.1c69fb81.5ba5b.6f86SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 316 bytes Desc: not available URL: -------------- next part -------------- From ea2245e7b5aae33d64063cf96db9332dc7f98d87 Mon Sep 17 00:00:00 2001 From: Saurabh299 <72179994+Saurabh299 at users.noreply.github.com> Date: Thu, 1 Oct 2020 09:56:26 +0530 Subject: [PATCH] improved the commons public license --- COPYING | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/COPYING b/COPYING index 7cdbe0b..90d3473 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Attribution-NonCommercial-ShareAlike 4.0 International +Attribution-Non Commercial-Share A like 4.0 International Commons Public License ======================================================================= From lxc-bot at linuxcontainers.org Thu Oct 1 08:55:05 2020 From: lxc-bot at linuxcontainers.org (kundanku505 on Github) Date: Thu, 01 Oct 2020 01:55:05 -0700 (PDT) Subject: [lxc-devel] [linuxcontainers.org/master] Improve Docs Message-ID: <5f759969.1c69fb81.61eca.5c2aSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 316 bytes Desc: not available URL: -------------- next part -------------- From be38d72b7525f0c256f8ca8910d2b40b8be7fd3c Mon Sep 17 00:00:00 2001 From: kundanku505 <72192719+kundanku505 at users.noreply.github.com> Date: Thu, 1 Oct 2020 14:24:05 +0530 Subject: [PATCH] Update generate --- generate | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generate b/generate index 365cf2c..e68a34b 100755 --- a/generate +++ b/generate @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python3 an amaging project # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2, as From lxc-bot at linuxcontainers.org Thu Oct 1 11:30:36 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Thu, 01 Oct 2020 04:30:36 -0700 (PDT) Subject: [lxc-devel] [lxd/master] VM: Adds support for live memory growth back to boot time memory size Message-ID: <5f75bddc.1c69fb81.32c88.b14fSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 391 bytes Desc: not available URL: -------------- next part -------------- From 124aad5233ee89720edd6c137500d8c81a300508 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 1 Oct 2020 12:25:37 +0100 Subject: [PATCH 1/5] lxd/instance/drivers/qmp/monitor: Renames GetMemoryBalloonSizeBytes And makes comments more accurate. Signed-off-by: Thomas Parrott --- lxd/instance/drivers/qmp/monitor.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/instance/drivers/qmp/monitor.go b/lxd/instance/drivers/qmp/monitor.go index 11c011da38..9d5eb789ff 100644 --- a/lxd/instance/drivers/qmp/monitor.go +++ b/lxd/instance/drivers/qmp/monitor.go @@ -323,8 +323,8 @@ func (m *Monitor) GetCPUs() ([]int, error) { return pids, nil } -// GetBalloonSizeBytes returns the current size of the memory balloon in bytes. -func (m *Monitor) GetBalloonSizeBytes() (int64, error) { +// GetMemoryBalloonSizeBytes returns effective size of the memory in bytes (considering the current balloon size). +func (m *Monitor) GetMemoryBalloonSizeBytes() (int64, error) { respRaw, err := m.qmp.Run([]byte("{'execute': 'query-balloon'}")) if err != nil { m.Disconnect() From 6f9663cc9d33bd27376c28155830eca3707b3072 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 1 Oct 2020 12:26:00 +0100 Subject: [PATCH 2/5] lxd/instance/drivers/qmp/monitor: Renames SetMemoryBalloonSizeBytes And makes comments more accurate. Signed-off-by: Thomas Parrott --- lxd/instance/drivers/qmp/monitor.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/instance/drivers/qmp/monitor.go b/lxd/instance/drivers/qmp/monitor.go index 9d5eb789ff..da917d78af 100644 --- a/lxd/instance/drivers/qmp/monitor.go +++ b/lxd/instance/drivers/qmp/monitor.go @@ -346,8 +346,8 @@ func (m *Monitor) GetMemoryBalloonSizeBytes() (int64, error) { return respDecoded.Return.Actual, nil } -// SetBalloonSizeBytes sets the size of the memory balloon in bytes. -func (m *Monitor) SetBalloonSizeBytes(sizeBytes int64) error { +// SetMemoryBalloonSizeBytes sets the size of the memory in bytes (which will resize the balloon as needed). +func (m *Monitor) SetMemoryBalloonSizeBytes(sizeBytes int64) error { respRaw, err := m.qmp.Run([]byte(fmt.Sprintf("{'execute': 'balloon', 'arguments': {'value': %d}}", sizeBytes))) if err != nil { m.Disconnect() From 6ced837edcae82aee518f31af32c13fd06d36d88 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 1 Oct 2020 12:26:24 +0100 Subject: [PATCH 3/5] lxd/instance/drivers/qmp/monitor: Adds GetMemorySizeBytes function Signed-off-by: Thomas Parrott --- lxd/instance/drivers/qmp/monitor.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lxd/instance/drivers/qmp/monitor.go b/lxd/instance/drivers/qmp/monitor.go index da917d78af..e02bc96590 100644 --- a/lxd/instance/drivers/qmp/monitor.go +++ b/lxd/instance/drivers/qmp/monitor.go @@ -323,6 +323,29 @@ func (m *Monitor) GetCPUs() ([]int, error) { return pids, nil } +// GetMemorySizeBytes returns the current size of the base memory in bytes. +func (m *Monitor) GetMemorySizeBytes() (int64, error) { + respRaw, err := m.qmp.Run([]byte("{'execute': 'query-memory-size-summary'}")) + if err != nil { + m.Disconnect() + return -1, ErrMonitorDisconnect + } + + // Process the response. + var respDecoded struct { + Return struct { + BaseMemory int64 `json:"base-memory"` + } `json:"return"` + } + + err = json.Unmarshal(respRaw, &respDecoded) + if err != nil { + return -1, ErrMonitorBadReturn + } + + return respDecoded.Return.BaseMemory, nil +} + // GetMemoryBalloonSizeBytes returns effective size of the memory in bytes (considering the current balloon size). func (m *Monitor) GetMemoryBalloonSizeBytes() (int64, error) { respRaw, err := m.qmp.Run([]byte("{'execute': 'query-balloon'}")) From 6c6cd366eeb1f5e0c3a6106f15975f8de6408f14 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 1 Oct 2020 12:26:49 +0100 Subject: [PATCH 4/5] lxd/instance/drivers/driver/qemu: Adds qemuDefaultMemSize constant Signed-off-by: Thomas Parrott --- lxd/instance/drivers/driver_qemu.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 3fed78efcb..af18d1dfe7 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -69,6 +69,9 @@ const qemuUnsafeIO = "unsafeio" // qemuSerialChardevName is used to communicate state via qmp between Qemu and LXD. const qemuSerialChardevName = "qemu_serial-chardev" +// qemuDefaultMemSize is the default memory size for VMs if not limit specified. +const qemuDefaultMemSize = "1GiB" + var errQemuAgentOffline = fmt.Errorf("LXD VM agent isn't currently running") var vmConsole = map[int]bool{} @@ -1924,7 +1927,7 @@ func (vm *qemu) addCPUMemoryConfig(sb *strings.Builder) error { // Configure memory limit. memSize := vm.expandedConfig["limits.memory"] if memSize == "" { - memSize = "1GiB" // Default to 1GiB if no memory limit specified. + memSize = qemuDefaultMemSize // Default if no memory limit specified. } memSizeBytes, err := units.ParseByteSizeString(memSize) From c71a3f8ec288b9c29eb8908db316614c1d0998ef Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 1 Oct 2020 12:27:24 +0100 Subject: [PATCH 5/5] lxd/instance/drivers/driver/qemu: Updates updateMemoryLimit to allow memory resize back to boot time size - Fixes bug where unsetting limits.memory didn't try and resize memory back to default size. - Allows live memory growth back to original boot time size. - Makes comments more accurate base on qemu's actual balloon size commands. Signed-off-by: Thomas Parrott --- lxd/instance/drivers/driver_qemu.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index af18d1dfe7..547e5fb088 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -2975,9 +2975,12 @@ func (vm *qemu) Update(args db.InstanceArgs, userRequested bool) error { return nil } -// updateMemoryLimit live updates the VM's memory limit by shrinking the balloon device. -// Only memory shrinking is supported at this time. +// updateMemoryLimit live updates the VM's memory limit by reszing the balloon device. func (vm *qemu) updateMemoryLimit(newLimit string) error { + if newLimit == "" { + newLimit = qemuDefaultMemSize + } + if shared.IsTrue(vm.expandedConfig["limits.memory.hugepages"]) { return fmt.Errorf("Cannot live update memory limit when using huge pages") } @@ -2994,27 +2997,32 @@ func (vm *qemu) updateMemoryLimit(newLimit string) error { return err // The VM isn't running as no monitor socket available. } - curSizeBytes, err := monitor.GetBalloonSizeBytes() + baseSizeBytes, err := monitor.GetMemorySizeBytes() + if err != nil { + return err + } + + curSizeBytes, err := monitor.GetMemoryBalloonSizeBytes() if err != nil { return err } if curSizeBytes == newSizeBytes { return nil - } else if curSizeBytes < newSizeBytes { - return fmt.Errorf("Cannot increase memory size when VM is running") + } else if baseSizeBytes < newSizeBytes { + return fmt.Errorf("Cannot increase memory size beyond boot time size when VM is running") } - // Shrink balloon device. - err = monitor.SetBalloonSizeBytes(newSizeBytes) + // Set effective memory size. + err = monitor.SetMemoryBalloonSizeBytes(newSizeBytes) if err != nil { return err } - // Shrinking the balloon can take time, so poll the actual balloon size to check it has shrunk within 1% + // Changing the memory balloon can take time, so poll the effectice size to check it has shrunk within 1% // of the target size, which we then take as success (it may still continue to shrink closer to target). for i := 0; i < 5; i++ { - curSizeBytes, err = monitor.GetBalloonSizeBytes() + curSizeBytes, err = monitor.GetMemoryBalloonSizeBytes() if err != nil { return err } From lxc-bot at linuxcontainers.org Thu Oct 1 15:13:20 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Thu, 01 Oct 2020 08:13:20 -0700 (PDT) Subject: [lxc-devel] [lxd/master] VM: Detect broken/hung qemu process and reflect in LXD state output Message-ID: <5f75f210.1c69fb81.7a051.2cf3SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 472 bytes Desc: not available URL: -------------- next part -------------- From c2ea7cef7d6cee39ad87acec8024f408f172202f Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 1 Oct 2020 16:07:03 +0100 Subject: [PATCH 1/2] lxd/instance/drivers/driver/qemu: Updates IsRunning to not check for BROKEN state As this can never ben returned from StatusCode.String() which is what vm.State() returns. Signed-off-by: Thomas Parrott --- lxd/instance/drivers/driver_qemu.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index b7c0360223..bbd4f1d899 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -4221,7 +4221,7 @@ func (vm *qemu) agentGetState() (*api.InstanceState, error) { // IsRunning returns whether or not the instance is running. func (vm *qemu) IsRunning() bool { state := vm.State() - return state != "BROKEN" && state != "STOPPED" + return state != "STOPPED" } // IsFrozen returns whether the instance frozen or not. From 5ad910fcdfcb63e2daab337d1fefa0ede0a6f6ff Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 1 Oct 2020 16:08:24 +0100 Subject: [PATCH 2/2] lxd/instance/drivers/driver/qemu: Updates statusCode() to detect if monitor failure with running VM If the QMP monitor cannot connect, but VM process mentioned in qemu.pid still exists, then return error state, as qemu has likely crashed/hung. Signed-off-by: Thomas Parrott --- lxd/instance/drivers/driver_qemu.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index bbd4f1d899..3ced883459 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -4304,6 +4304,13 @@ func (vm *qemu) statusCode() api.StatusCode { // Connect to the monitor. monitor, err := qmp.Connect(vm.monitorPath(), qemuSerialChardevName, vm.getMonitorEventHandler()) if err != nil { + // If cannot connect to monitor, but qemu process in pid file still exists, then likely qemu + // has crashed/hung and this instance is in an error state. + pid, _ := vm.pid() + if pid > 0 && shared.PathExists(fmt.Sprintf("/proc/%d", pid)) { + return api.Error + } + // If we fail to connect, chances are the VM isn't running. return api.Stopped } From lxc-bot at linuxcontainers.org Thu Oct 1 16:12:36 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Thu, 01 Oct 2020 09:12:36 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/apparmor: Allow access to zoneinfo files Message-ID: <5f75fff4.1c69fb81.5f6fc.25baSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 375 bytes Desc: not available URL: -------------- next part -------------- From 4950044cd42f5425c746da494b7e59bfd32833b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 1 Oct 2020 12:10:24 -0400 Subject: [PATCH] lxd/apparmor: Allow access to zoneinfo files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported in #7935 Signed-off-by: Stéphane Graber --- lxd/apparmor/network_dnsmasq.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lxd/apparmor/network_dnsmasq.go b/lxd/apparmor/network_dnsmasq.go index ef2c5ef691..e77efd5029 100644 --- a/lxd/apparmor/network_dnsmasq.go +++ b/lxd/apparmor/network_dnsmasq.go @@ -37,6 +37,7 @@ profile "{{ .name }}" flags=(attach_disconnected,mediate_deleted) { # Additional system files @{PROC}/sys/net/ipv6/conf/*/mtu r, @{PROC}/@{pid}/fd/ r, + {{ .rootPath }}/usr/share/zoneinfo/** r, # System configuration access {{ .rootPath }}/etc/gai.conf r, From lxc-bot at linuxcontainers.org Thu Oct 1 16:50:26 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Thu, 01 Oct 2020 09:50:26 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: Add EUI64 guessing for bridged NIC state Message-ID: <5f7608d2.1c69fb81.e8055.5343SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 9a0cf626779c141f59b358963cede5632c988563 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 1 Oct 2020 17:07:39 +0100 Subject: [PATCH 1/3] lxd/network/network/utils: Adds GetNeighbourV6Addresses function Signed-off-by: Thomas Parrott --- lxd/network/network_utils.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go index c195228a3d..a4ead17f6f 100644 --- a/lxd/network/network_utils.go +++ b/lxd/network/network_utils.go @@ -805,6 +805,34 @@ func GetHostDevice(parent string, vlan string) string { return defaultVlan } +// GetNeighbourV6Addresses returns the IPv6 addresses in the neighbour cache for a particular interface and MAC. +func GetNeighbourV6Addresses(interfaceName string, hwaddr string) ([]net.IP, error) { + addresses := []net.IP{} + + // Look for neighbour entries for IPv6. + out, err := shared.RunCommand("ip", "-6", "neigh", "show", "dev", interfaceName) + if err == nil { + for _, line := range strings.Split(out, "\n") { + // Split fields and early validation. + fields := strings.Fields(line) + if len(fields) != 4 { + continue + } + + if fields[2] != hwaddr { + continue + } + + ip := net.ParseIP(fields[0]) + if ip != nil { + addresses = append(addresses, ip) + } + } + } + + return addresses, nil +} + // GetLeaseAddresses returns the lease addresses for a network and hwaddr. func GetLeaseAddresses(s *state.State, networkName string, hwaddr string) ([]api.InstanceStateNetworkAddress, error) { addresses := []api.InstanceStateNetworkAddress{} From 6ba197ad54e2dff9416ff698cdbbc2c6e836aa1c Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 1 Oct 2020 17:46:42 +0100 Subject: [PATCH 2/3] lxd/network/network/utils: Updates GetLeaseAddresses to return only net.IP list - Removes IPv6 neighbour look as was out of scope for this function's name. - Returns []net.IP rather than []api.InstanceStateNetworkAddress. Signed-off-by: Thomas Parrott --- lxd/network/network_utils.go | 79 ++++-------------------------------- 1 file changed, 9 insertions(+), 70 deletions(-) diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go index a4ead17f6f..96d57465ed 100644 --- a/lxd/network/network_utils.go +++ b/lxd/network/network_utils.go @@ -834,48 +834,10 @@ func GetNeighbourV6Addresses(interfaceName string, hwaddr string) ([]net.IP, err } // GetLeaseAddresses returns the lease addresses for a network and hwaddr. -func GetLeaseAddresses(s *state.State, networkName string, hwaddr string) ([]api.InstanceStateNetworkAddress, error) { - addresses := []api.InstanceStateNetworkAddress{} - - // Look for neighborhood entries for IPv6. - out, err := shared.RunCommand("ip", "-6", "neigh", "show", "dev", networkName) - if err == nil { - for _, line := range strings.Split(out, "\n") { - // Split fields and early validation. - fields := strings.Fields(line) - if len(fields) != 4 { - continue - } - - if fields[2] != hwaddr { - continue - } - - // Prepare the entry. - addr := api.InstanceStateNetworkAddress{} - addr.Address = fields[0] - addr.Family = "inet6" - - if strings.HasPrefix(fields[0], "fe80::") { - addr.Scope = "link" - } else { - addr.Scope = "global" - } - - addresses = append(addresses, addr) - } - } - - // Look for DHCP leases. +func GetLeaseAddresses(networkName string, hwaddr string) ([]net.IP, error) { leaseFile := shared.VarPath("networks", networkName, "dnsmasq.leases") if !shared.PathExists(leaseFile) { - return addresses, nil - } - - // Pass project.Default here, as currently dnsmasq (bridged) networks do not support projects. - dbInfo, err := LoadByName(s, project.Default, networkName) - if err != nil { - return nil, err + return nil, fmt.Errorf("Leases file not found for network %q", networkName) } content, err := ioutil.ReadFile(leaseFile) @@ -883,13 +845,15 @@ func GetLeaseAddresses(s *state.State, networkName string, hwaddr string) ([]api return nil, err } + addresses := []net.IP{} + for _, lease := range strings.Split(string(content), "\n") { fields := strings.Fields(lease) if len(fields) < 5 { continue } - // Parse the MAC + // Parse the MAC. mac := GetMACSlice(fields[1]) macStr := strings.Join(mac, ":") @@ -901,36 +865,11 @@ func GetLeaseAddresses(s *state.State, networkName string, hwaddr string) ([]api continue } - // Parse the IP - addr := api.InstanceStateNetworkAddress{ - Address: fields[2], - Scope: "global", - } - - ip := net.ParseIP(addr.Address) - if ip == nil { - continue - } - - if ip.To4() != nil { - addr.Family = "inet" - - _, subnet, _ := net.ParseCIDR(dbInfo.Config()["ipv4.address"]) - if subnet != nil { - mask, _ := subnet.Mask.Size() - addr.Netmask = fmt.Sprintf("%d", mask) - } - } else { - addr.Family = "inet6" - - _, subnet, _ := net.ParseCIDR(dbInfo.Config()["ipv6.address"]) - if subnet != nil { - mask, _ := subnet.Mask.Size() - addr.Netmask = fmt.Sprintf("%d", mask) - } + // Parse the IP. + ip := net.ParseIP(fields[2]) + if ip != nil { + addresses = append(addresses, ip) } - - addresses = append(addresses, addr) } return addresses, nil From 5d2b5543d6a62aa4d77e4a3a8fedc59765045fc6 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 1 Oct 2020 17:21:54 +0100 Subject: [PATCH 3/3] lxd/device/nic/bridged: Updates State() to return partial data - Only tries to parse dnsmasq leases file if parent network is managed. - If parent network is not managed, no longer treat this as complete failure. - Try and find IPv6 addresses from IP neighbour cache using network.GetNeighbourV6Addresses. - Load interface MTU using NIC's `host_name` rather than parent interface (should be the same but more consistent with how OVN NIC does it). - Also means that if the NIC's host veth interface is missing, then this call will not return any results. - If interface is available, will return interface stats even with no IPs found. - Guess link-local address netmask size based on IP family. Signed-off-by: Thomas Parrott --- lxd/device/nic_bridged.go | 77 ++++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 10 deletions(-) diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go index f8a255e4d8..47b0d67379 100644 --- a/lxd/device/nic_bridged.go +++ b/lxd/device/nic_bridged.go @@ -1091,24 +1091,81 @@ func (d *nicBridged) State() (*api.InstanceStateNetwork, error) { // Populate device config with volatile fields if needed. networkVethFillFromVolatile(d.config, v) - if d.config["hwaddr"] == "" { - return nil, nil + ips := []net.IP{} + + // Check if parent is managed network and load config. + // Pass project.Default here, as currently dnsmasq (bridged) networks do not support projects. + n, err := network.LoadByName(d.state, project.Default, d.config["parent"]) + if err == nil && d.config["hwaddr"] != "" { + // Parse the leases file if parent network is managed. + leaseIPs, err := network.GetLeaseAddresses(n.Name(), d.config["hwaddr"]) + if err == nil { + for _, leaseIP := range leaseIPs { + ips = append(ips, leaseIP) + } + } } - // Parse the leases file. - addresses, err := network.GetLeaseAddresses(d.state, d.config["parent"], d.config["hwaddr"]) - if err != nil { - return nil, err + // Get IPv6 addresses from IP neighbour cache if present. + neighIPs, err := network.GetNeighbourV6Addresses(d.config["parent"], d.config["hwaddr"]) + if err == nil { + for _, neighIP := range neighIPs { + ips = append(ips, neighIP) + } + } + + // Extract subnet sizes from bridge addresses if available. + var v4mask string + var v6mask string + + if n != nil { + netConfig := n.Config() + _, v4subnet, _ := net.ParseCIDR(netConfig["ipv4.address"]) + _, v6subnet, _ := net.ParseCIDR(netConfig["ipv6.address"]) + + if v4subnet != nil { + mask, _ := v4subnet.Mask.Size() + v4mask = fmt.Sprintf("%d", mask) + } + + if v6subnet != nil { + mask, _ := v6subnet.Mask.Size() + v6mask = fmt.Sprintf("%d", mask) + } } - if len(addresses) == 0 { - return nil, nil + // Convert IPs to InstanceStateNetworkAddresses. + addresses := []api.InstanceStateNetworkAddress{} + for _, ip := range ips { + addr := api.InstanceStateNetworkAddress{} + addr.Address = ip.String() + addr.Family = "inet" + addr.Netmask = v4mask + + if ip.To4() == nil { + addr.Family = "inet6" + addr.Netmask = v6mask + } + + if ip.IsLinkLocalUnicast() { + addr.Scope = "link" + + if addr.Family == "inet6" { + addr.Netmask = "64" // Link-local IPv6 addresses are /64. + } else { + addr.Netmask = "16" // Local-local IPv4 addresses are /16. + } + } else { + addr.Scope = "global" + } + + addresses = append(addresses, addr) } // Get MTU. - iface, err := net.InterfaceByName(d.config["parent"]) + iface, err := net.InterfaceByName(d.config["host_name"]) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "Failed getting host interface state") } // Retrieve the host counters, as we report the values from the instance's point of view, From lxc-bot at linuxcontainers.org Thu Oct 1 19:21:04 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Thu, 01 Oct 2020 12:21:04 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/apparmor: Add /etc/localtime to the list Message-ID: <5f762c20.1c69fb81.4a737.4410SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From e821b245948d6257515a586390de3b1e9c0ec603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 1 Oct 2020 15:20:40 -0400 Subject: [PATCH] lxd/apparmor: Add /etc/localtime to the list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/apparmor/network_dnsmasq.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lxd/apparmor/network_dnsmasq.go b/lxd/apparmor/network_dnsmasq.go index e77efd5029..bedf098395 100644 --- a/lxd/apparmor/network_dnsmasq.go +++ b/lxd/apparmor/network_dnsmasq.go @@ -37,6 +37,7 @@ profile "{{ .name }}" flags=(attach_disconnected,mediate_deleted) { # Additional system files @{PROC}/sys/net/ipv6/conf/*/mtu r, @{PROC}/@{pid}/fd/ r, + {{ .rootPath }}/etc/localtime r, {{ .rootPath }}/usr/share/zoneinfo/** r, # System configuration access From lxc-bot at linuxcontainers.org Thu Oct 1 19:23:59 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Thu, 01 Oct 2020 12:23:59 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/project: Always allow cloud-init:config drives Message-ID: <5f762ccf.1c69fb81.b2fbd.646fSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From 426a43f47025b9205ec36c1bf3931d09d40a681f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 1 Oct 2020 15:23:29 -0400 Subject: [PATCH] lxd/project: Always allow cloud-init:config drives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/project/permissions.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lxd/project/permissions.go b/lxd/project/permissions.go index e635490b05..3950396bb6 100644 --- a/lxd/project/permissions.go +++ b/lxd/project/permissions.go @@ -403,6 +403,11 @@ func checkRestrictions(project *api.Project, instances []db.Instance, profiles [ return nil } + // Always allow the cloud-init config drive. + if device["path"] == "" && device["source"] == "cloud-init:config" { + return nil + } + switch restrictionValue { case "block": return fmt.Errorf("Disk devices are forbidden") From lxc-bot at linuxcontainers.org Thu Oct 1 23:24:59 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Thu, 01 Oct 2020 16:24:59 -0700 (PDT) Subject: [lxc-devel] [lxd/master] doc/image-handling: Cover publishing Message-ID: <5f76654b.1c69fb81.75839.b78dSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From 113fa6a719aab6c612148968c1b39bceb838f4b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 1 Oct 2020 19:24:45 -0400 Subject: [PATCH] doc/image-handling: Cover publishing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- doc/image-handling.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/image-handling.md b/doc/image-handling.md index c9222208dc..92c852c6d3 100644 --- a/doc/image-handling.md +++ b/doc/image-handling.md @@ -83,6 +83,19 @@ On the client side, this is used with: `lxc image import URL --alias some-name` +### Publishing an instance or snapshot as a new image +An instance or one of its snapshots can be turned into a new image. +This is done on the CLI with `lxc publish`. + +When doing this, you will most likely first want to cleanup metadata and +templats on the instance you're publishing using the `lxc config metadata` +and `lxc config template` commands. You will also want to remove any +instance-specific state like host SSH keys, dbus/systemd machine-id, ... + +The publishing process can take quite a while as a tarball must be +generated from the instance and then be compressed. As this can be +particularly I/O and CPU intensive, publish operations are serialized by LXD. + ## Caching When spawning an instance from a remote image, the remote image is downloaded into the local image store with the cached bit set. The image From lxc-bot at linuxcontainers.org Fri Oct 2 11:26:46 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Fri, 02 Oct 2020 04:26:46 -0700 (PDT) Subject: [lxc-devel] [lxc-ci/master] OVN: Project restriction tests Message-ID: <5f770e76.1c69fb81.51451.3031SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 303 bytes Desc: not available URL: -------------- next part -------------- From 44db93c7f29602301ce006afd06aaa15152ed32e Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 2 Oct 2020 11:17:26 +0100 Subject: [PATCH 1/2] bin/test-lxd-ovn: Use 127.0.0.1 for geneve encapsulation For single node test no need to depend on external IP. Signed-off-by: Thomas Parrott --- bin/test-lxd-ovn | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/test-lxd-ovn b/bin/test-lxd-ovn index 612a50e..294c306 100755 --- a/bin/test-lxd-ovn +++ b/bin/test-lxd-ovn @@ -36,11 +36,10 @@ apt install ovn-host ovn-central --yes # Configure OVN set -x -IP=$(ip -4 route get 8.8.8.8 | grep src | cut -d' ' -f7) ovs-vsctl set open_vswitch . \ external_ids:ovn-remote=unix:/var/run/ovn/ovnsb_db.sock \ external_ids:ovn-encap-type=geneve \ - external_ids:ovn-encap-ip=${IP} + external_ids:ovn-encap-ip=127.0.0.1 # Configure LXD lxc storage create default zfs From 3289890cac42041c9ad52520eaa2fc3ba38a846d Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 2 Oct 2020 12:25:41 +0100 Subject: [PATCH 2/2] bin/test-lxd-ovn: Adds tests for project restrictions and more thorough clean up steps Allows the test to be re-run multiple times if needed. Signed-off-by: Thomas Parrott --- bin/test-lxd-ovn | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/bin/test-lxd-ovn b/bin/test-lxd-ovn index 294c306..e8261ea 100755 --- a/bin/test-lxd-ovn +++ b/bin/test-lxd-ovn @@ -52,14 +52,17 @@ lxc network create lxdbr0 \ ipv6.address=fd42:4242:4242:1010::1/64 ipv6.nat=true \ ipv6.ovn.ranges=fd42:4242:4242:1010::200-fd42:4242:4242:1010::254 -lxc network create ovn-virtual-network network=lxdbr0 --type=ovn +# Create OVN network without specifying uplink parent network (check default selection works). +lxc network create ovn-virtual-network --type=ovn # Test set +x lxc network list +lxc project switch default echo "==> Launching a test container on lxdbr0" lxc init images:ubuntu/20.04 u1 +FINGERPRINT="$(lxc image ls -cf --format=csv)" lxc config device add u1 eth0 nic network=lxdbr0 name=eth0 lxc start u1 @@ -109,6 +112,26 @@ echo "==> DNS resolution on OVN" lxc exec u3 -- ping -c1 -4 u2.lxd lxc exec u3 -- ping -c1 -6 u2.lxd +echo "===> Testing project restrictions" +lxc project create testovn -c features.networks=true -c restricted=true + +# Test we cannot create network in restricted project with no defined uplinks. +! lxc network create ovn-virtual-network --project testovn + +# Test we can create network with a single restricted uplink network defined without specfiying it (or type). +lxc project set testovn restricted.networks.uplinks=lxdbr0 +lxc network create ovn-virtual-network --project testovn +lxc network delete ovn-virtual-network --project testovn + +# Test we have to specify uplink network if multiple are allowed. +lxc network create lxdbr1 --project default +lxc project set testovn restricted.networks.uplinks=lxdbr0,lxdbr1 +! lxc network create ovn-virtual-network --project testovn +lxc network create ovn-virtual-network network=lxdbr0 --project testovn +lxc network delete ovn-virtual-network --project testovn +lxc project delete testovn +lxc network delete lxdbr1 --project default + echo "===> Testing projects" lxc project create testovn -c features.networks=true -c limits.networks=1 lxc project switch testovn @@ -184,4 +207,12 @@ lxc delete -f u2 u3 lxc network delete ovn-virtual-network lxc network delete lxdbr0 --project default +lxc image delete "${FINGERPRINT}" --project testovn +lxc image delete "${FINGERPRINT}" --project default +lxc profile device remove default root --project testovn +lxc profile device remove default root --project default +lxc storage delete default +lxc project switch default +lxc project delete testovn + FAIL=0 From lxc-bot at linuxcontainers.org Sat Oct 3 09:19:27 2020 From: lxc-bot at linuxcontainers.org (sladkani on Github) Date: Sat, 03 Oct 2020 02:19:27 -0700 (PDT) Subject: [lxc-devel] [lxd/master] shared/simplestreams: Fix stream's index download url Message-ID: <5f78421f.1c69fb81.64a39.54e7SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 787 bytes Desc: not available URL: -------------- next part -------------- From 961a1c019ea700cbbc692686979b20fd2f489b0b Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Thu, 1 Oct 2020 12:51:53 +0300 Subject: [PATCH] shared/simplestreams: Fix stream's index download url Since commit aef8f1928 ("shared/simplestreams: Implement caching support") the index url contains double '/' between the host part and the path part, so the resulting url looks like: https://STREAMSERVER//streams/v1/index.json Certain servers will literaly take '/streams/v1/index.json' as the path, and therefore return 404 or 403. Fix parseStream(), by removing the prepended '/' passed to s.cachedDownload(). Signed-off-by: Shmulik Ladkani --- shared/simplestreams/simplestreams.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/simplestreams/simplestreams.go b/shared/simplestreams/simplestreams.go index 59841b419e..fd420326a3 100644 --- a/shared/simplestreams/simplestreams.go +++ b/shared/simplestreams/simplestreams.go @@ -148,7 +148,7 @@ func (s *SimpleStreams) parseStream() (*Stream, error) { return s.cachedStream, nil } - body, err := s.cachedDownload("/streams/v1/index.json") + body, err := s.cachedDownload("streams/v1/index.json") if err != nil { return nil, err } From lxc-bot at linuxcontainers.org Sat Oct 3 16:32:58 2020 From: lxc-bot at linuxcontainers.org (komish on Github) Date: Sat, 03 Oct 2020 09:32:58 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Prevent empty passwords during init if password authentication is enabled Message-ID: <5f78a7ba.1c69fb81.d1997.d25cSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 989 bytes Desc: not available URL: -------------- next part -------------- From 98193b9ee0b79a2e3dc2312384964993eb5d7245 Mon Sep 17 00:00:00 2001 From: "Jose R. Gonzalez" Date: Sat, 3 Oct 2020 07:36:22 -0500 Subject: [PATCH] refuse empty passwords Signed-off-by: Jose R. Gonzalez --- shared/cmd/ask.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/shared/cmd/ask.go b/shared/cmd/ask.go index a8eace979e..eb5edd078b 100644 --- a/shared/cmd/ask.go +++ b/shared/cmd/ask.go @@ -110,7 +110,8 @@ func AskPassword(question string) string { inSecond := string(pwd) inSecond = strings.TrimSuffix(inSecond, "\n") - if inFirst == inSecond { + // refuse empty password or if password inputs do not match + if len(inFirst) > 0 && inFirst == inSecond { return inFirst } @@ -122,11 +123,19 @@ func AskPassword(question string) string { // // It's the same as AskPassword, but it won't ask to enter it again. func AskPasswordOnce(question string) string { - fmt.Printf(question) - pwd, _ := terminal.ReadPassword(0) - fmt.Println("") + for { + fmt.Printf(question) + pwd, _ := terminal.ReadPassword(0) + fmt.Println("") - return string(pwd) + // refuse empty password + spwd := string(pwd) + if len(spwd) > 0 { + return spwd + } + + invalidInput() + } } // Ask a question on the output stream and read the answer from the input stream From lxc-bot at linuxcontainers.org Sat Oct 3 17:16:32 2020 From: lxc-bot at linuxcontainers.org (simos on Github) Date: Sat, 03 Oct 2020 10:16:32 -0700 (PDT) Subject: [lxc-devel] [distrobuilder/master] Updated doc/index.md Message-ID: <5f78b1f0.1c69fb81.8726d.98dbSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 331 bytes Desc: not available URL: -------------- next part -------------- From fa6a27907a285ee8e89ca53e1b2e06fda7a5bc85 Mon Sep 17 00:00:00 2001 From: Simos Xenitellis Date: Sat, 3 Oct 2020 09:44:53 +0300 Subject: [PATCH 1/2] Cosmetic update to the README.me (index.md) --- doc/index.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/doc/index.md b/doc/index.md index 5065ea1..229ae71 100644 --- a/doc/index.md +++ b/doc/index.md @@ -32,6 +32,8 @@ Flags: --cleanup Clean up cache directory (default true) -h, --help help for distrobuilder -o, --options Override options (list of key=value) + -t, --timeout Timeout in seconds + --version Print version number Use "distrobuilder [command] --help" for more information about a command. ``` @@ -45,7 +47,7 @@ In the following, we see how to create a container image for LXD. `distrobuilder` is available from the snapstore. ``` -snap install distrobuilder --classic +sudo snap install distrobuilder --classic ``` ## Installing from source @@ -77,6 +79,12 @@ make cd ``` +Finally, you can run `distrobuilder` as follows. You may also add to your $PATH the directory `$HOME/go/bin/` so that you do not need to run the command with the full path. + +``` +$HOME/go/bin/distrobuilder +``` + ### Creating a container image To create a container image, first create a directory where you will be placing the container images, and enter that directory. From 6e4756ba29a4b75af4d007595ff933c4b7313018 Mon Sep 17 00:00:00 2001 From: Simos Xenitellis Date: Sat, 3 Oct 2020 20:14:56 +0300 Subject: [PATCH 2/2] Updated instructions Updated instructions to generate a container image. --- doc/index.md | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/doc/index.md b/doc/index.md index 229ae71..dd88631 100644 --- a/doc/index.md +++ b/doc/index.md @@ -112,10 +112,10 @@ If the command is successful, you will get an output similar to the following. T ```bash $ ls -l -total 121032 --rw-r--r-- 1 root root 560 Oct 3 13:28 lxd.tar.xz --rw-r--r-- 1 root root 123928576 Oct 3 13:28 rootfs.squashfs --rw-rw-r-- 1 multipass multipass 3317 Oct 3 13:19 ubuntu.yaml +total 100960 +-rw-r--r-- 1 root root 676 Oct 3 16:15 lxd.tar.xz +-rw-r--r-- 1 root root 103370752 Oct 3 16:15 rootfs.squashfs +-rw-r--r-- 1 ubuntu ubuntu 7449 Oct 3 16:03 ubuntu.yaml $ ``` @@ -125,18 +125,18 @@ To add the container image to a LXD installation, use the `lxc image import` com ```bash $ lxc image import lxd.tar.xz rootfs.squashfs --alias mycontainerimage -Image imported with fingerprint: ae81c04327b5b115383a4f90b969c97f5ef417e02d4210d40cbb17a038729a27 +Image imported with fingerprint: 009349195858651a0f883de804e64eb82e0ac8c0bc51880 ``` -Let's see the container image in LXD. The `ubuntu.yaml` had a setting to create an Ubuntu 17.10 (`artful`) image. The size is 118MB. +Let's see the container image in LXD. The `ubuntu.yaml` had a setting to create an Ubuntu 20.04 (`focal`) image. The size is 98.58MB. ```bash $ lxc image list mycontainerimage -+------------------+--------------+--------+---------------+--------+----------+------------------------------+ -| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE | -+------------------+--------------+--------+---------------+--------+----------+------------------------------+ -| mycontainerimage | ae81c04327b5 | no | Ubuntu artful | x86_64 | 118.19MB | Oct 3, 2018 at 12:09pm (UTC) | -+------------------+--------------+--------+---------------+--------+----------+------------------------------+ ++------------------+--------------+--------+--------------+--------+---------+-----------------------------+ +| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE | ++------------------+--------------+--------+--------------+--------+---------+-----------------------------+ +| mycontainerimage | 009349195858 | no | Ubuntu focal | x86_64 | 98.58MB | Oct 3, 2020 at 5:10pm (UTC) | ++------------------+--------------+--------+--------------+--------+---------+-----------------------------+ ``` ### Launching a LXD container from the container image @@ -182,3 +182,17 @@ lxc-start -n myContainerImage ### Examples Examples of yaml files for various distributions can be found in the [examples directory](./doc/examples) and in the [lxc-ci repository](https://github.com/lxc/lxc-ci/tree/master/images). + +### Troubleshooting + +#### Error "Cannot install into target '/var/cache/distrobuilder.123456789/rootfs' mounted with noexec or nodev" + +You have installed `distrobuilder` into a LXD container and you are trying to run it. `distrobuilder` does not run in a LXD container. Run `distrobuilder` on the host, or in a VM. + +##### Error "error: This revision of snap "distrobuilder" was published using classic confinement" + +You are trying to install the `distrobuilder` snap package. The `distrobuilder` snap package has been configured to use the `classic` confinement. Therefore, when you install it, you have to add the flag `--classic` as shown above in the instructions. + +##### Error "You must be root to run this tool" + +You must be _root_ in order to run the `distrobuilder` tool. The tool runs commands such as `mknod` that require administrative privileges. Prepend `sudo` when running `distrobuilder`. From lxc-bot at linuxcontainers.org Sat Oct 3 20:01:14 2020 From: lxc-bot at linuxcontainers.org (drs-11 on Github) Date: Sat, 03 Oct 2020 13:01:14 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/storage: Add rsync.compression to storagePoolConfigKeys Message-ID: <5f78d88a.1c69fb81.c3fbf.75bcSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 796 bytes Desc: not available URL: -------------- next part -------------- From 858f589003cb244856d500b51571b93cca73e240 Mon Sep 17 00:00:00 2001 From: drs-11 Date: Sun, 4 Oct 2020 00:15:40 +0530 Subject: [PATCH] lxd/storage: Adds rsync.compression config key to storagePoolConfigKeys Signed-off-by: D R Siddhartha --- doc/api-extensions.md | 4 ++++ doc/storage.md | 1 + lxd/storage/drivers/driver_btrfs.go | 10 +++++++++- lxd/storage/drivers/driver_ceph.go | 10 +++++++++- lxd/storage/drivers/driver_cephfs.go | 12 +++++++++++- lxd/storage/drivers/driver_common.go | 11 ++++++++++- lxd/storage/drivers/driver_zfs.go | 10 +++++++++- lxd/storage_pools_config.go | 3 +++ shared/version/api.go | 1 + test/suites/migration.sh | 7 +++++++ 10 files changed, 64 insertions(+), 5 deletions(-) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 52be415f36..159d86d419 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -1176,3 +1176,7 @@ This includes the following new endpoints (see [RESTful API](rest-api.md) for de The following existing endpoint has been modified: * `POST /1.0/storage-pools///` accepts the new source type `backup` + +## storage\_rsync\_compression +Adds `rsync.compression` config key to storage pools. This key can be used +to disable compression in rsync while migrating storage pools. \ No newline at end of file diff --git a/doc/storage.md b/doc/storage.md index 6e0dd9f5fa..67dc5786f1 100644 --- a/doc/storage.md +++ b/doc/storage.md @@ -27,6 +27,7 @@ lvm.vg.force\_reuse | bool | lvm driver volume.lvm.stripes | string | lvm driver | - | storage\_lvm\_stripes | Number of stripes to use for new volumes (or thin pool volume). volume.lvm.stripes.size | string | lvm driver | - | storage\_lvm\_stripes | Size of stripes to use (at least 4096 bytes and multiple of 512bytes). rsync.bwlimit | string | - | 0 (no limit) | storage\_rsync\_bwlimit | Specifies the upper limit to be placed on the socket I/O whenever rsync has to be used to transfer storage entities. +rsync.compression | bool | appropriate driver | true | storage\_rsync\_compression | Whether to use compression while migrating storage pools. volatile.initial\_source | string | - | - | storage\_volatile\_initial\_source | Records the actual source passed during creating (e.g. /dev/sdb). volatile.pool.pristine | string | - | true | storage\_driver\_ceph | Whether the pool has been empty on creation time. volume.block.filesystem | string | block based driver (lvm) | ext4 | storage | Filesystem to use for new volumes diff --git a/lxd/storage/drivers/driver_btrfs.go b/lxd/storage/drivers/driver_btrfs.go index 988e741277..d50d80366d 100644 --- a/lxd/storage/drivers/driver_btrfs.go +++ b/lxd/storage/drivers/driver_btrfs.go @@ -372,9 +372,17 @@ func (d *btrfs) GetResources() (*api.ResourcesStoragePool, error) { // MigrationType returns the type of transfer methods to be used when doing migrations between pools in preference order. func (d *btrfs) MigrationTypes(contentType ContentType, refresh bool) []migration.Type { - rsyncFeatures := []string{"xattrs", "delete", "compress", "bidirectional"} + var rsyncFeatures []string btrfsFeatures := []string{migration.BTRFSFeatureMigrationHeader, migration.BTRFSFeatureSubvolumes} + // Do not pass compression argument to rsync if the assoicated + // config key, that is rsync.compression, is set to false. + if d.Config()["rsync.compression"] != "" && !shared.IsTrue(d.Config()["rsync.compression"]) { + rsyncFeatures = []string{"xattrs", "delete", "bidirectional"} + } else { + rsyncFeatures = []string{"xattrs", "delete", "compress", "bidirectional"} + } + // Only offer rsync for refreshes or if running in an unprivileged container. if refresh || d.state.OS.RunningInUserNS { var transportType migration.MigrationFSType diff --git a/lxd/storage/drivers/driver_ceph.go b/lxd/storage/drivers/driver_ceph.go index f13f1a3c0e..17b16339c5 100644 --- a/lxd/storage/drivers/driver_ceph.go +++ b/lxd/storage/drivers/driver_ceph.go @@ -340,7 +340,15 @@ func (d *ceph) GetResources() (*api.ResourcesStoragePool, error) { // MigrationType returns the type of transfer methods to be used when doing migrations between pools in preference order. func (d *ceph) MigrationTypes(contentType ContentType, refresh bool) []migration.Type { - rsyncFeatures := []string{"delete", "compress", "bidirectional"} + var rsyncFeatures []string + + // Do not pass compression argument to rsync if the assoicated + // config key, that is rsync.compression, is set to false. + if d.Config()["rsync.compression"] != "" && !shared.IsTrue(d.Config()["rsync.compression"]) { + rsyncFeatures = []string{"delete", "bidirectional"} + } else { + rsyncFeatures = []string{"delete", "compress", "bidirectional"} + } if refresh { var transportType migration.MigrationFSType diff --git a/lxd/storage/drivers/driver_cephfs.go b/lxd/storage/drivers/driver_cephfs.go index d275212c45..fe70d1698b 100644 --- a/lxd/storage/drivers/driver_cephfs.go +++ b/lxd/storage/drivers/driver_cephfs.go @@ -312,6 +312,16 @@ func (d *cephfs) GetResources() (*api.ResourcesStoragePool, error) { // MigrationTypes returns the supported migration types and options supported by the driver. func (d *cephfs) MigrationTypes(contentType ContentType, refresh bool) []migration.Type { + var rsyncFeatures []string + + // Do not pass compression argument to rsync if the assoicated + // config key, that is rsync.compression, is set to false. + if d.Config()["rsync.compression"] != "" && !shared.IsTrue(d.Config()["rsync.compression"]) { + rsyncFeatures = []string{"delete", "bidirectional"} + } else { + rsyncFeatures = []string{"delete", "compress", "bidirectional"} + } + if contentType != ContentTypeFS { return nil } @@ -320,7 +330,7 @@ func (d *cephfs) MigrationTypes(contentType ContentType, refresh bool) []migrati return []migration.Type{ { FSType: migration.MigrationFSType_RSYNC, - Features: []string{"delete", "compress", "bidirectional"}, + Features: rsyncFeatures, }, } } diff --git a/lxd/storage/drivers/driver_common.go b/lxd/storage/drivers/driver_common.go index 843f5652f0..10c880f8bd 100644 --- a/lxd/storage/drivers/driver_common.go +++ b/lxd/storage/drivers/driver_common.go @@ -132,6 +132,15 @@ func (d *common) validateVolume(vol Volume, driverRules map[string]func(value st // in preference order. func (d *common) MigrationTypes(contentType ContentType, refresh bool) []migration.Type { var transportType migration.MigrationFSType + var rsyncFeatures []string + + // Do not pass compression argument to rsync if the assoicated + // config key, that is rsync.compression, is set to false. + if d.Config()["rsync.compression"] != "" && !shared.IsTrue(d.Config()["rsync.compression"]) { + rsyncFeatures = []string{"xattrs", "delete", "bidirectional"} + } else { + rsyncFeatures = []string{"xattrs", "delete", "compress", "bidirectional"} + } if contentType == ContentTypeBlock { transportType = migration.MigrationFSType_BLOCK_AND_RSYNC @@ -142,7 +151,7 @@ func (d *common) MigrationTypes(contentType ContentType, refresh bool) []migrati return []migration.Type{ { FSType: transportType, - Features: []string{"xattrs", "delete", "compress", "bidirectional"}, + Features: rsyncFeatures, }, } } diff --git a/lxd/storage/drivers/driver_zfs.go b/lxd/storage/drivers/driver_zfs.go index 387a351f5f..1ddc78facc 100644 --- a/lxd/storage/drivers/driver_zfs.go +++ b/lxd/storage/drivers/driver_zfs.go @@ -452,7 +452,15 @@ func (d *zfs) GetResources() (*api.ResourcesStoragePool, error) { // MigrationType returns the type of transfer methods to be used when doing migrations between pools in preference order. func (d *zfs) MigrationTypes(contentType ContentType, refresh bool) []migration.Type { - rsyncFeatures := []string{"xattrs", "delete", "compress", "bidirectional"} + var rsyncFeatures []string + + // Do not pass compression argument to rsync if the assoicated + // config key, that is rsync.compression, is set to false. + if d.Config()["rsync.compression"] != "" && !shared.IsTrue(d.Config()["rsync.compression"]) { + rsyncFeatures = []string{"xattrs", "delete", "bidirectional"} + } else { + rsyncFeatures = []string{"xattrs", "delete", "compress", "bidirectional"} + } // When performing a refresh, always use rsync. Using zfs send/receive // here doesn't make sense since it would need to send everything again diff --git a/lxd/storage_pools_config.go b/lxd/storage_pools_config.go index cfcdf0b234..5c6892bd12 100644 --- a/lxd/storage_pools_config.go +++ b/lxd/storage_pools_config.go @@ -81,6 +81,9 @@ var storagePoolConfigKeys = map[string]func(value string) error{ "zfs.clone_copy": validate.Optional(validate.IsBool), "zfs.pool_name": validate.IsAny, "rsync.bwlimit": validate.IsAny, + + // valid drivers: btrfs, ceph, cephfs, zfs + "rsync.compression": validate.Optional(validate.IsBool), } func storagePoolValidateConfig(name string, driver string, config map[string]string, oldConfig map[string]string) error { diff --git a/shared/version/api.go b/shared/version/api.go index dfe5156e54..f893568048 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -227,6 +227,7 @@ var APIExtensions = []string{ "projects_networks", "projects_networks_restricted_uplinks", "custom_volume_backup", + "storage_rsync_compress", } // APIExtensionsCount returns the number of available API extensions. diff --git a/test/suites/migration.sh b/test/suites/migration.sh index 6b3896f117..f2416eed6b 100644 --- a/test/suites/migration.sh +++ b/test/suites/migration.sh @@ -329,6 +329,13 @@ migration() { lxc_remote storage volume delete l2:"$remote_pool2" vol5 lxc_remote storage volume delete l2:"$remote_pool2" vol6 + # Test migration when rsync compression is disabled + lxc_remote storage set l1:"$remote_pool1" rsync.compression false + lxc_remote storage volume create l1:"$remote_pool1" foo + lxc_remote storage volume copy l1:"$remote_pool1"/foo l2:"$remote_pool2"/bar + lxc_remote storage volume delete l1:"$remote_pool1" foo + lxc_remote storage volume delete l2:"$remote_pool2" bar + # Test some migration between projects lxc_remote project create l1:proj -c features.images=false -c features.profiles=false lxc_remote project switch l1:proj From lxc-bot at linuxcontainers.org Sun Oct 4 16:27:20 2020 From: lxc-bot at linuxcontainers.org (drs-11 on Github) Date: Sun, 04 Oct 2020 09:27:20 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Adds config key for disabling rsync compression Message-ID: <5f79f7e8.1c69fb81.d66af.9f6cSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 407 bytes Desc: not available URL: -------------- next part -------------- From 34097139ae42fc62082adb6ed8e7b2415b3a0b30 Mon Sep 17 00:00:00 2001 From: D R Siddhartha Date: Sun, 4 Oct 2020 21:40:53 +0530 Subject: [PATCH 1/4] lxd/storage: Adds rsync.compression config key Signed-off-by: D R Siddhartha --- lxd/storage/drivers/driver_btrfs.go | 10 +++++++++- lxd/storage/drivers/driver_ceph.go | 10 +++++++++- lxd/storage/drivers/driver_cephfs.go | 12 +++++++++++- lxd/storage/drivers/driver_common.go | 11 ++++++++++- lxd/storage/drivers/driver_zfs.go | 10 +++++++++- lxd/storage/utils.go | 1 + lxd/storage_pools_config.go | 3 +++ 7 files changed, 52 insertions(+), 5 deletions(-) diff --git a/lxd/storage/drivers/driver_btrfs.go b/lxd/storage/drivers/driver_btrfs.go index 988e741277..c98b8409de 100644 --- a/lxd/storage/drivers/driver_btrfs.go +++ b/lxd/storage/drivers/driver_btrfs.go @@ -372,9 +372,17 @@ func (d *btrfs) GetResources() (*api.ResourcesStoragePool, error) { // MigrationType returns the type of transfer methods to be used when doing migrations between pools in preference order. func (d *btrfs) MigrationTypes(contentType ContentType, refresh bool) []migration.Type { - rsyncFeatures := []string{"xattrs", "delete", "compress", "bidirectional"} + var rsyncFeatures []string btrfsFeatures := []string{migration.BTRFSFeatureMigrationHeader, migration.BTRFSFeatureSubvolumes} + // Do not pass compression argument to rsync if the associated + // config key, that is rsync.compression, is set to false. + if d.Config()["rsync.compression"] != "" && !shared.IsTrue(d.Config()["rsync.compression"]) { + rsyncFeatures = []string{"xattrs", "delete", "bidirectional"} + } else { + rsyncFeatures = []string{"xattrs", "delete", "compress", "bidirectional"} + } + // Only offer rsync for refreshes or if running in an unprivileged container. if refresh || d.state.OS.RunningInUserNS { var transportType migration.MigrationFSType diff --git a/lxd/storage/drivers/driver_ceph.go b/lxd/storage/drivers/driver_ceph.go index f13f1a3c0e..2500bb94ac 100644 --- a/lxd/storage/drivers/driver_ceph.go +++ b/lxd/storage/drivers/driver_ceph.go @@ -340,7 +340,15 @@ func (d *ceph) GetResources() (*api.ResourcesStoragePool, error) { // MigrationType returns the type of transfer methods to be used when doing migrations between pools in preference order. func (d *ceph) MigrationTypes(contentType ContentType, refresh bool) []migration.Type { - rsyncFeatures := []string{"delete", "compress", "bidirectional"} + var rsyncFeatures []string + + // Do not pass compression argument to rsync if the associated + // config key, that is rsync.compression, is set to false. + if d.Config()["rsync.compression"] != "" && !shared.IsTrue(d.Config()["rsync.compression"]) { + rsyncFeatures = []string{"delete", "bidirectional"} + } else { + rsyncFeatures = []string{"delete", "compress", "bidirectional"} + } if refresh { var transportType migration.MigrationFSType diff --git a/lxd/storage/drivers/driver_cephfs.go b/lxd/storage/drivers/driver_cephfs.go index d275212c45..015da2c949 100644 --- a/lxd/storage/drivers/driver_cephfs.go +++ b/lxd/storage/drivers/driver_cephfs.go @@ -312,6 +312,16 @@ func (d *cephfs) GetResources() (*api.ResourcesStoragePool, error) { // MigrationTypes returns the supported migration types and options supported by the driver. func (d *cephfs) MigrationTypes(contentType ContentType, refresh bool) []migration.Type { + var rsyncFeatures []string + + // Do not pass compression argument to rsync if the associated + // config key, that is rsync.compression, is set to false. + if d.Config()["rsync.compression"] != "" && !shared.IsTrue(d.Config()["rsync.compression"]) { + rsyncFeatures = []string{"delete", "bidirectional"} + } else { + rsyncFeatures = []string{"delete", "compress", "bidirectional"} + } + if contentType != ContentTypeFS { return nil } @@ -320,7 +330,7 @@ func (d *cephfs) MigrationTypes(contentType ContentType, refresh bool) []migrati return []migration.Type{ { FSType: migration.MigrationFSType_RSYNC, - Features: []string{"delete", "compress", "bidirectional"}, + Features: rsyncFeatures, }, } } diff --git a/lxd/storage/drivers/driver_common.go b/lxd/storage/drivers/driver_common.go index 843f5652f0..e15950acad 100644 --- a/lxd/storage/drivers/driver_common.go +++ b/lxd/storage/drivers/driver_common.go @@ -132,6 +132,15 @@ func (d *common) validateVolume(vol Volume, driverRules map[string]func(value st // in preference order. func (d *common) MigrationTypes(contentType ContentType, refresh bool) []migration.Type { var transportType migration.MigrationFSType + var rsyncFeatures []string + + // Do not pass compression argument to rsync if the associated + // config key, that is rsync.compression, is set to false. + if d.Config()["rsync.compression"] != "" && !shared.IsTrue(d.Config()["rsync.compression"]) { + rsyncFeatures = []string{"xattrs", "delete", "bidirectional"} + } else { + rsyncFeatures = []string{"xattrs", "delete", "compress", "bidirectional"} + } if contentType == ContentTypeBlock { transportType = migration.MigrationFSType_BLOCK_AND_RSYNC @@ -142,7 +151,7 @@ func (d *common) MigrationTypes(contentType ContentType, refresh bool) []migrati return []migration.Type{ { FSType: transportType, - Features: []string{"xattrs", "delete", "compress", "bidirectional"}, + Features: rsyncFeatures, }, } } diff --git a/lxd/storage/drivers/driver_zfs.go b/lxd/storage/drivers/driver_zfs.go index 387a351f5f..5e290af54f 100644 --- a/lxd/storage/drivers/driver_zfs.go +++ b/lxd/storage/drivers/driver_zfs.go @@ -452,7 +452,15 @@ func (d *zfs) GetResources() (*api.ResourcesStoragePool, error) { // MigrationType returns the type of transfer methods to be used when doing migrations between pools in preference order. func (d *zfs) MigrationTypes(contentType ContentType, refresh bool) []migration.Type { - rsyncFeatures := []string{"xattrs", "delete", "compress", "bidirectional"} + var rsyncFeatures []string + + // Do not pass compression argument to rsync if the associated + // config key, that is rsync.compression, is set to false. + if d.Config()["rsync.compression"] != "" && !shared.IsTrue(d.Config()["rsync.compression"]) { + rsyncFeatures = []string{"xattrs", "delete", "bidirectional"} + } else { + rsyncFeatures = []string{"xattrs", "delete", "compress", "bidirectional"} + } // When performing a refresh, always use rsync. Using zfs send/receive // here doesn't make sense since it would need to send everything again diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go index 93201ad46e..6f4c9e8c0b 100644 --- a/lxd/storage/utils.go +++ b/lxd/storage/utils.go @@ -424,6 +424,7 @@ func validatePoolCommonRules() map[string]func(string) error { "volume.size": validate.Optional(validate.IsSize), "size": validate.Optional(validate.IsSize), "rsync.bwlimit": validate.IsAny, + "rsync.compression": validate.Optional(validate.IsBool), } } diff --git a/lxd/storage_pools_config.go b/lxd/storage_pools_config.go index cfcdf0b234..5c6892bd12 100644 --- a/lxd/storage_pools_config.go +++ b/lxd/storage_pools_config.go @@ -81,6 +81,9 @@ var storagePoolConfigKeys = map[string]func(value string) error{ "zfs.clone_copy": validate.Optional(validate.IsBool), "zfs.pool_name": validate.IsAny, "rsync.bwlimit": validate.IsAny, + + // valid drivers: btrfs, ceph, cephfs, zfs + "rsync.compression": validate.Optional(validate.IsBool), } func storagePoolValidateConfig(name string, driver string, config map[string]string, oldConfig map[string]string) error { From f371c6c931afca0a033a345c9116efc91fa8e2ae Mon Sep 17 00:00:00 2001 From: D R Siddhartha Date: Sun, 4 Oct 2020 21:44:06 +0530 Subject: [PATCH 2/4] doc: Adds rsync.compression Signed-off-by: D R Siddhartha --- doc/storage.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/storage.md b/doc/storage.md index 6e0dd9f5fa..67dc5786f1 100644 --- a/doc/storage.md +++ b/doc/storage.md @@ -27,6 +27,7 @@ lvm.vg.force\_reuse | bool | lvm driver volume.lvm.stripes | string | lvm driver | - | storage\_lvm\_stripes | Number of stripes to use for new volumes (or thin pool volume). volume.lvm.stripes.size | string | lvm driver | - | storage\_lvm\_stripes | Size of stripes to use (at least 4096 bytes and multiple of 512bytes). rsync.bwlimit | string | - | 0 (no limit) | storage\_rsync\_bwlimit | Specifies the upper limit to be placed on the socket I/O whenever rsync has to be used to transfer storage entities. +rsync.compression | bool | appropriate driver | true | storage\_rsync\_compression | Whether to use compression while migrating storage pools. volatile.initial\_source | string | - | - | storage\_volatile\_initial\_source | Records the actual source passed during creating (e.g. /dev/sdb). volatile.pool.pristine | string | - | true | storage\_driver\_ceph | Whether the pool has been empty on creation time. volume.block.filesystem | string | block based driver (lvm) | ext4 | storage | Filesystem to use for new volumes From f9b21d257a4e8deb79b36d1540d151b4d14bc242 Mon Sep 17 00:00:00 2001 From: D R Siddhartha Date: Sun, 4 Oct 2020 21:46:45 +0530 Subject: [PATCH 3/4] api: storage_rsync_compression Signed-off-by: D R Siddhartha --- doc/api-extensions.md | 4 ++++ shared/version/api.go | 1 + 2 files changed, 5 insertions(+) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index a0cd6ec30c..d7c233c3e6 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -1182,3 +1182,7 @@ Adds `Name` field to `InstanceBackupArgs` to allow specifying a different instan Adds `Name` and `PoolName` fields to `StoragePoolVolumeBackupArgs` to allow specifying a different volume name when restoring a custom volume backup. + +## storage\_rsync\_compression +Adds `rsync.compression` config key to storage pools. This key can be used +to disable compression in rsync while migrating storage pools. \ No newline at end of file diff --git a/shared/version/api.go b/shared/version/api.go index 41a64fac67..ba9380b676 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -228,6 +228,7 @@ var APIExtensions = []string{ "projects_networks_restricted_uplinks", "custom_volume_backup", "backup_override_name", + "storage_rsync_compression", } // APIExtensionsCount returns the number of available API extensions. From 1d0da811313c7c0e1ab3bd6449ea9ad591ad34d0 Mon Sep 17 00:00:00 2001 From: D R Siddhartha Date: Sun, 4 Oct 2020 21:48:35 +0530 Subject: [PATCH 4/4] tests: Valid rsync.compression Signed-off-by: D R Siddhartha --- test/suites/migration.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/suites/migration.sh b/test/suites/migration.sh index 6b3896f117..f2416eed6b 100644 --- a/test/suites/migration.sh +++ b/test/suites/migration.sh @@ -329,6 +329,13 @@ migration() { lxc_remote storage volume delete l2:"$remote_pool2" vol5 lxc_remote storage volume delete l2:"$remote_pool2" vol6 + # Test migration when rsync compression is disabled + lxc_remote storage set l1:"$remote_pool1" rsync.compression false + lxc_remote storage volume create l1:"$remote_pool1" foo + lxc_remote storage volume copy l1:"$remote_pool1"/foo l2:"$remote_pool2"/bar + lxc_remote storage volume delete l1:"$remote_pool1" foo + lxc_remote storage volume delete l2:"$remote_pool2" bar + # Test some migration between projects lxc_remote project create l1:proj -c features.images=false -c features.profiles=false lxc_remote project switch l1:proj From lxc-bot at linuxcontainers.org Sun Oct 4 18:19:33 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Sun, 04 Oct 2020 11:19:33 -0700 (PDT) Subject: [lxc-devel] [lxd/master] doc/index: Add libsqlite3-dev back to dependencies Message-ID: <5f7a1235.1c69fb81.40083.a7ddSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 410 bytes Desc: not available URL: -------------- next part -------------- From a2280ad171bceac0cc2fa02e68e810553f854223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Sun, 4 Oct 2020 14:18:56 -0400 Subject: [PATCH] doc/index: Add libsqlite3-dev back to dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we don't ship our own anymore. Closes #7978 Signed-off-by: Stéphane Graber --- doc/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/index.md b/doc/index.md index 767f49cebf..07fba98869 100644 --- a/doc/index.md +++ b/doc/index.md @@ -42,7 +42,7 @@ later to work. On ubuntu, you can get those with: ```bash sudo apt update -sudo apt install acl autoconf dnsmasq-base git golang libacl1-dev libcap-dev liblxc1 liblxc-dev libtool libudev-dev libuv1-dev make pkg-config rsync squashfs-tools tar tcl xz-utils ebtables +sudo apt install acl autoconf dnsmasq-base git golang libacl1-dev libcap-dev liblxc1 liblxc-dev libsqlite3-dev libtool libudev-dev libuv1-dev make pkg-config rsync squashfs-tools tar tcl xz-utils ebtables ``` Note that when building LXC yourself, ensure to build it with the appropriate From lxc-bot at linuxcontainers.org Mon Oct 5 09:01:41 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Mon, 05 Oct 2020 02:01:41 -0700 (PDT) Subject: [lxc-devel] [lxd/master] nftables: Updates nft parser to handle nft sets with composite `type` field Message-ID: <5f7ae0f5.1c69fb81.5acc.8fc8SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 409 bytes Desc: not available URL: -------------- next part -------------- From 406d531d63b89ed48b5fcbd1067c8ab2d9619457 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 5 Oct 2020 09:56:10 +0100 Subject: [PATCH] lxd/firewall/drivers/driver/nftables: Updates nft parser to handle nft sets with composite `type` field Fixes https://github.com/lxc/lxd/issues/7973 Signed-off-by: Thomas Parrott --- lxd/firewall/drivers/drivers_nftables.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lxd/firewall/drivers/drivers_nftables.go b/lxd/firewall/drivers/drivers_nftables.go index 9bdeebef1f..7a781fc971 100644 --- a/lxd/firewall/drivers/drivers_nftables.go +++ b/lxd/firewall/drivers/drivers_nftables.go @@ -96,7 +96,7 @@ func (d Nftables) Compat() (bool, error) { } for _, item := range ruleset { - if item.Type == "rule" { + if item.ItemType == "rule" { return true, nil // At least one rule found indicates in use. } } @@ -106,11 +106,11 @@ func (d Nftables) Compat() (bool, error) { // nftGenericItem represents some common fields amongst the different nftables types. type nftGenericItem struct { - Type string // Type of item (table, chain or rule). - Family string `json:"family"` // Family of item (ip, ip6, bridge etc). - Table string `json:"table"` // Table the item belongs to (for chains and rules). - Chain string `json:"chain"` // Chain the item belongs to (for rules). - Name string `json:"name"` // Name of item (for tables and chains). + ItemType string `json:"-"` // Type of item (table, chain or rule). Populated by LXD. + Family string `json:"family"` // Family of item (ip, ip6, bridge etc). + Table string `json:"table"` // Table the item belongs to (for chains and rules). + Chain string `json:"chain"` // Chain the item belongs to (for rules). + Name string `json:"name"` // Name of item (for tables and chains). } // nftParseRuleset parses the ruleset and returns the generic parts as a slice of items. @@ -140,13 +140,13 @@ func (d Nftables) nftParseRuleset() ([]nftGenericItem, error) { items := []nftGenericItem{} for _, item := range v.Nftables { if rule, found := item["rule"]; found { - rule.Type = "rule" + rule.ItemType = "rule" items = append(items, rule) } else if chain, found := item["chain"]; found { - chain.Type = "chain" + chain.ItemType = "chain" items = append(items, chain) } else if table, found := item["table"]; found { - table.Type = "table" + table.ItemType = "table" items = append(items, table) } } @@ -477,7 +477,7 @@ func (d Nftables) removeChains(families []string, chainSuffix string, chains ... for _, family := range families { for _, item := range ruleset { - if item.Type == "chain" && item.Family == family && item.Table == nftablesNamespace && shared.StringInSlice(item.Name, fullChains) { + if item.ItemType == "chain" && item.Family == family && item.Table == nftablesNamespace && shared.StringInSlice(item.Name, fullChains) { _, err = shared.RunCommand("nft", "flush", "chain", family, nftablesNamespace, item.Name, ";", "delete", "chain", family, nftablesNamespace, item.Name) if err != nil { return errors.Wrapf(err, "Failed deleting nftables chain %q (%s)", item.Name, family) From lxc-bot at linuxcontainers.org Mon Oct 5 09:13:26 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Mon, 05 Oct 2020 02:13:26 -0700 (PDT) Subject: [lxc-devel] [lxd/master] validate: Increases IsNetworkMTU max MTU to 16384 to support super jumbo packets Message-ID: <5f7ae3b6.1c69fb81.6104d.9d5bSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 409 bytes Desc: not available URL: -------------- next part -------------- From 84299fc473794c1b77a182f0f8f26ec2f856c5b5 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 5 Oct 2020 10:11:49 +0100 Subject: [PATCH] shared/validate/validate: Increases max MTU to 16384 to support super jumbo packets Fixes https://github.com/lxc/lxd/issues/7972 Signed-off-by: Thomas Parrott --- shared/validate/validate.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/validate/validate.go b/shared/validate/validate.go index d9645f6dfc..356a7d8744 100644 --- a/shared/validate/validate.go +++ b/shared/validate/validate.go @@ -385,7 +385,7 @@ func IsNetworkVLAN(value string) error { return nil } -// IsNetworkMTU validates MTU number >= 1280 and <= 9202. +// IsNetworkMTU validates MTU number >= 1280 and <= 16384. // Anything below 68 and the kernel doesn't allow IPv4, anything below 1280 and the kernel doesn't allow IPv6. // So require an IPv6-compatible MTU as the low value and cap at the max ethernet jumbo frame size. func IsNetworkMTU(value string) error { @@ -394,8 +394,8 @@ func IsNetworkMTU(value string) error { return fmt.Errorf("Invalid MTU %q", value) } - if mtu < 1280 || mtu > 9202 { - return fmt.Errorf("Out of MTU range (1280-9202) %q", value) + if mtu < 1280 || mtu > 16384 { + return fmt.Errorf("Out of MTU range (1280-16384) %q", value) } return nil From lxc-bot at linuxcontainers.org Mon Oct 5 16:29:49 2020 From: lxc-bot at linuxcontainers.org (brauner on Github) Date: Mon, 05 Oct 2020 09:29:49 -0700 (PDT) Subject: [lxc-devel] [lxd/master] shift_linux: vendor posix_acl_xattr.h Message-ID: <5f7b49fd.1c69fb81.f3b71.2bfcSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 1130 bytes Desc: not available URL: -------------- next part -------------- From a5e40f8791afb55d91984a9934c895e261aaa349 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 5 Oct 2020 18:25:20 +0200 Subject: [PATCH] shift_linux: vendor posix_acl_xattr.h -bash-4.2$ make go get -t -v -d ./... CC=gcc go install -v -tags "libsqlite3" ./... gopkg.in/lxc/go-lxc.v2 github.com/canonical/go-dqlite/internal/bindings github.com/lxc/lxd/shared/idmap # github.com/lxc/lxd/shared/idmap shared/idmap/shift_linux.go:32:35: fatal error: linux/posix_acl_xattr.h: No such file or directory #include ^ compilation terminated. # github.com/canonical/go-dqlite/internal/bindings /bin/ld: cannot find -lraft /bin/ld: cannot find -ldqlite collect2: error: ld returned 1 exit status make: *** [default] Error 2 -bash-4.2$ # Steps to reproduce 1. try to build on an older kernel system (e.g., rhel7) Closes #7982. Signed-off-by: Christian Brauner --- lxd/include/lxd_posix_acl_xattr.h | 39 +++++++++++++++++++++++++++++++ shared/idmap/shift_linux.go | 8 +++---- 2 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 lxd/include/lxd_posix_acl_xattr.h diff --git a/lxd/include/lxd_posix_acl_xattr.h b/lxd/include/lxd_posix_acl_xattr.h new file mode 100644 index 0000000000..506380f0b9 --- /dev/null +++ b/lxd/include/lxd_posix_acl_xattr.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-2.1+ WITH Linux-syscall-note */ +/* + * Copyright (C) 2002 Andreas Gruenbacher + * Copyright (C) 2016 Red Hat, Inc. + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#ifndef __UAPI_POSIX_ACL_XATTR_H +#define __UAPI_POSIX_ACL_XATTR_H + +#include + +/* Supported ACL a_version fields */ +#define POSIX_ACL_XATTR_VERSION 0x0002 + +/* An undefined entry e_id value */ +#define ACL_UNDEFINED_ID (-1) + +struct posix_acl_xattr_entry { + __le16 e_tag; + __le16 e_perm; + __le32 e_id; +}; + +struct posix_acl_xattr_header { + __le32 a_version; +}; + +#endif /* __UAPI_POSIX_ACL_XATTR_H */ diff --git a/shared/idmap/shift_linux.go b/shared/idmap/shift_linux.go index 05fdd6cd6f..c61f534ca6 100644 --- a/shared/idmap/shift_linux.go +++ b/shared/idmap/shift_linux.go @@ -29,7 +29,6 @@ import ( #include #include #include -#include #include #include #include @@ -37,13 +36,14 @@ import ( #include #include #include -#include + +#include "../../lxd/include/lxd_posix_acl_xattr.h" +#include "../../lxd/include/memory_utils.h" // Needs to be included at the end +#include #include -#include "../../lxd/include/memory_utils.h" - #ifndef VFS_CAP_REVISION_1 #define VFS_CAP_REVISION_1 0x01000000 #endif From lxc-bot at linuxcontainers.org Mon Oct 5 20:31:34 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Mon, 05 Oct 2020 13:31:34 -0700 (PDT) Subject: [lxc-devel] [lxd/master] AppArmor tweaks for forkproxy Message-ID: <5f7b82a6.1c69fb81.8d01b.baebSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 0142c545185608c3f58218456fe31dd1ef981d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 5 Oct 2020 16:30:26 -0400 Subject: [PATCH 1/2] lxd/apparmor/forkproxy: Fix bad profile name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/apparmor/instance_forkproxy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/apparmor/instance_forkproxy.go b/lxd/apparmor/instance_forkproxy.go index 58c146af28..ef95ab3efc 100644 --- a/lxd/apparmor/instance_forkproxy.go +++ b/lxd/apparmor/instance_forkproxy.go @@ -135,7 +135,7 @@ func forkproxyProfile(state *state.State, inst instance, dev device) (string, er func ForkproxyProfileName(inst instance, dev device) string { path := shared.VarPath("") name := fmt.Sprintf("%s_%s_<%s>", dev.Name(), project.Instance(inst.Project(), inst.Name()), path) - return profileName("", name) + return profileName("forkproxy", name) } // forkproxyProfileFilename returns the name of the on-disk profile name. From f14db6a7f16b7ab98fba00171cbe91773562aa9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 5 Oct 2020 16:30:52 -0400 Subject: [PATCH 2/2] lxd/apparmor/forkproxy: Allow writing to log path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/apparmor/instance_forkproxy.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lxd/apparmor/instance_forkproxy.go b/lxd/apparmor/instance_forkproxy.go index ef95ab3efc..05fe662460 100644 --- a/lxd/apparmor/instance_forkproxy.go +++ b/lxd/apparmor/instance_forkproxy.go @@ -47,6 +47,7 @@ profile "{{ .name }}" flags=(attach_disconnected,mediate_deleted) { network unix stream, # Forkproxy operation + {{ .logPath }}/** rw, @{PROC}/** rw, / rw, ptrace (read), @@ -121,6 +122,7 @@ func forkproxyProfile(state *state.State, inst instance, dev device) (string, er "rootPath": rootPath, "snap": shared.InSnap(), "exePath": util.GetExecPath(), + "logPath": inst.LogPath(), "libraryPath": strings.Split(os.Getenv("LD_LIBRARY_PATH"), ":"), "sockets": sockets, }) From lxc-bot at linuxcontainers.org Mon Oct 5 22:26:38 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Mon, 05 Oct 2020 15:26:38 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxc: Better handle copy/move between projects Message-ID: <5f7b9d9e.1c69fb81.5b653.d34aSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From 43ec7184b1987399b37459618ff68d4a8f6dce8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 5 Oct 2020 18:22:21 -0400 Subject: [PATCH] lxc: Better handle copy/move between projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxc/copy.go | 2 +- lxc/move.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lxc/copy.go b/lxc/copy.go index be3d576f68..7b88270ee4 100644 --- a/lxc/copy.go +++ b/lxc/copy.go @@ -403,7 +403,7 @@ func (c *cmdCopy) copyInstance(conf *config.Config, sourceResource string, destR } // If choosing a random name, show it to the user - if destResource == "" { + if destResource == "" && c.flagTargetProject == "" { // Get the successful operation data opInfo, err := op.GetTarget() if err != nil { diff --git a/lxc/move.go b/lxc/move.go index 182021c0f4..8a1b812976 100644 --- a/lxc/move.go +++ b/lxc/move.go @@ -64,7 +64,7 @@ func (c *cmdMove) Run(cmd *cobra.Command, args []string) error { conf := c.global.conf // Sanity checks - if c.flagTarget == "" { + if c.flagTarget == "" && c.flagTargetProject == "" { exit, err := c.global.CheckArgs(cmd, args, 2, 2) if exit { return err From lxc-bot at linuxcontainers.org Tue Oct 6 03:08:15 2020 From: lxc-bot at linuxcontainers.org (DDEFISHER on Github) Date: Mon, 05 Oct 2020 20:08:15 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd: Prevent internal cluster migration of instances with backups Message-ID: <5f7bdf9f.1c69fb81.c5e1f.93cdSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 313 bytes Desc: not available URL: -------------- next part -------------- From d8054daeb459c9fb4bf8ef128abde3dccc18a323 Mon Sep 17 00:00:00 2001 From: "daniel.defisher" Date: Sun, 4 Oct 2020 14:56:08 -0400 Subject: [PATCH] lxd: Prevent internal cluster migration of instances with backups --- lxd/instance_post.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lxd/instance_post.go b/lxd/instance_post.go index eb7b307a5b..a9a7eb035e 100644 --- a/lxd/instance_post.go +++ b/lxd/instance_post.go @@ -173,6 +173,15 @@ func containerPost(d *Daemon, r *http.Request) response.Response { if req.Migration { if targetNode != "" { + // Check if instance has backups. + backups, err := d.cluster.GetInstanceBackups(project, name) + if err != nil { + return response.SmartError(err) + } + if len(backups) > 0 { + return response.BadRequest(fmt.Errorf("Instance has backups")) + } + // Check whether the container is running. if !sourceNodeOffline && inst.IsRunning() { return response.BadRequest(fmt.Errorf("Container is running")) From lxc-bot at linuxcontainers.org Tue Oct 6 09:06:22 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Tue, 06 Oct 2020 02:06:22 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/apparmor: Fix version parsing Message-ID: <5f7c338e.1c69fb81.d708.0298SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 491 bytes Desc: not available URL: -------------- next part -------------- From d02107da5e37a6e4c397d71290e083b31f2773a3 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Tue, 6 Oct 2020 10:58:31 +0200 Subject: [PATCH] lxd/apparmor: Fix version parsing Instead of using NewDottedVersion(), use Parse() which can handle version strings containing suffixes, e.g. "3.0.0-beta1" or similar. Signed-off-by: Thomas Hipp --- lxd/apparmor/apparmor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/apparmor/apparmor.go b/lxd/apparmor/apparmor.go index 2231964633..9d8ec55f46 100644 --- a/lxd/apparmor/apparmor.go +++ b/lxd/apparmor/apparmor.go @@ -203,7 +203,7 @@ func getVersion(state *state.State) (*version.DottedVersion, error) { } fields := strings.Fields(strings.Split(out, "\n")[0]) - return version.NewDottedVersion(fields[len(fields)-1]) + return version.Parse(fields[len(fields)-1]) } // getCacheDir returns the applicable AppArmor cache directory. From lxc-bot at linuxcontainers.org Tue Oct 6 13:05:57 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Tue, 06 Oct 2020 06:05:57 -0700 (PDT) Subject: [lxc-devel] [distrobuilder/master] sources: Fix CentOS 8-Stream rootfs Message-ID: <5f7c6bb5.1c69fb81.4c6b2.e1bbSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 310 bytes Desc: not available URL: -------------- next part -------------- From 1085380d0f316aa379b06cee78c0321dcff8302f Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Tue, 6 Oct 2020 15:05:05 +0200 Subject: [PATCH] sources: Fix CentOS 8-Stream rootfs Signed-off-by: Thomas Hipp --- sources/centos-http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/centos-http.go b/sources/centos-http.go index e580c92..7a8f052 100644 --- a/sources/centos-http.go +++ b/sources/centos-http.go @@ -348,7 +348,7 @@ fi # Create a minimal rootfs mkdir /rootfs -yum ${yum_args} --installroot=/rootfs -y --releasever=%s install basesystem centos-release yum +yum ${yum_args} --installroot=/rootfs -y --releasever=%s --skip-broken install basesystem centos-release yum rm -rf /rootfs/var/cache/yum `, gpgKeysPath, s.majorVersion)) if err != nil { From lxc-bot at linuxcontainers.org Tue Oct 6 13:30:11 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Tue, 06 Oct 2020 06:30:11 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: OVN restructure to accommodate different uplink network types Message-ID: <5f7c7163.1c69fb81.6997c.ea8cSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 327 bytes Desc: not available URL: -------------- next part -------------- From 27fbdd30a059a98051180ac7cee574548a05125b Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 2 Oct 2020 15:30:54 +0100 Subject: [PATCH 01/13] lxd/networks: Log error in doNetworksCreate after failed create if cleanup fails too Signed-off-by: Thomas Parrott --- lxd/networks.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lxd/networks.go b/lxd/networks.go index ae67d059a8..09ea358629 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -456,7 +456,11 @@ func doNetworksCreate(d *Daemon, projectName string, req api.NetworksPost, clien if clientType != cluster.ClientTypeJoiner { err = n.Start() if err != nil { - n.Delete(clientType) + delErr := n.Delete(clientType) + if delErr != nil { + logger.Errorf("Failed clearing up network %q after failed create: %v", n.Name(), delErr) + } + return err } } From 4a50691e278f8bf9219669072ce27f83fc677bcd Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 2 Oct 2020 15:51:40 +0100 Subject: [PATCH 02/13] lxd/network/driver: Improve missing parent network error message Signed-off-by: Thomas Parrott --- lxd/network/driver_macvlan.go | 2 +- lxd/network/driver_sriov.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/network/driver_macvlan.go b/lxd/network/driver_macvlan.go index 8f82680e35..2b4903670b 100644 --- a/lxd/network/driver_macvlan.go +++ b/lxd/network/driver_macvlan.go @@ -29,7 +29,7 @@ func (n *macvlan) DBType() db.NetworkType { // Validate network config. func (n *macvlan) Validate(config map[string]string) error { rules := map[string]func(value string) error{ - "parent": validInterfaceName, + "parent": validate.Required(validate.IsNotEmpty, validInterfaceName), "mtu": validate.Optional(validate.IsNetworkMTU), "vlan": validate.Optional(validate.IsNetworkVLAN), "maas.subnet.ipv4": validate.IsAny, diff --git a/lxd/network/driver_sriov.go b/lxd/network/driver_sriov.go index 698242778f..54ead8c60c 100644 --- a/lxd/network/driver_sriov.go +++ b/lxd/network/driver_sriov.go @@ -29,7 +29,7 @@ func (n *sriov) DBType() db.NetworkType { // Validate network config. func (n *sriov) Validate(config map[string]string) error { rules := map[string]func(value string) error{ - "parent": validInterfaceName, + "parent": validate.Required(validate.IsNotEmpty, validInterfaceName), "mtu": validate.Optional(validate.IsNetworkMTU), "vlan": validate.Optional(validate.IsNetworkVLAN), "maas.subnet.ipv4": validate.IsAny, From 36c2637c3259268e62dcec5c66dacb2630d31f08 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 2 Oct 2020 14:52:56 +0100 Subject: [PATCH 03/13] lxd/network/driver/ovn: Moves uplink type agnostic parent port allocation logic into allocateParentPortIPs() Ready to accomodate other types of uplink network types. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index c05910891f..f4ab603101 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -371,8 +371,6 @@ func (n *ovn) setupParentPort(routerMAC net.HardwareAddr) (*ovnParentVars, error // setupParentPortBridge allocates external IPs on the parent bridge. // Returns the derived ovnParentVars settings. func (n *ovn) setupParentPortBridge(parentNet Network, routerMAC net.HardwareAddr) (*ovnParentVars, error) { - v := &ovnParentVars{} - bridgeNet, ok := parentNet.(*bridge) if !ok { return nil, fmt.Errorf("Network is not bridge type") @@ -383,19 +381,32 @@ func (n *ovn) setupParentPortBridge(parentNet Network, routerMAC net.HardwareAdd return nil, errors.Wrapf(err, "Network %q is not suitable for use as OVN parent", bridgeNet.name) } + v, err := n.allocateParentPortIPs(parentNet, "ipv4.address", "ipv6.address", routerMAC) + if err != nil { + return nil, errors.Wrapf(err, "Failed allocating parent port IPs on network", parentNet.Name()) + } + + return v, nil +} + +// allocateParentPortIPs attempts to find a free IP in the parent network's OVN ranges and then stores it in +// ovnVolatileParentIPv4 and ovnVolatileParentIPv6 config keys on this network. Returns ovnParentVars settings. +func (n *ovn) allocateParentPortIPs(parentNet Network, v4CIDRKey string, v6CIDRKey string, routerMAC net.HardwareAddr) (*ovnParentVars, error) { + v := &ovnParentVars{} + parentNetConf := parentNet.Config() // Parent derived settings. v.extSwitchProviderName = parentNet.Name() // Optional parent values. - parentIPv4, parentIPv4Net, err := net.ParseCIDR(parentNetConf["ipv4.address"]) + parentIPv4, parentIPv4Net, err := net.ParseCIDR(parentNetConf[v4CIDRKey]) if err == nil { v.dnsIPv4 = parentIPv4 v.routerExtGwIPv4 = parentIPv4 } - parentIPv6, parentIPv6Net, err := net.ParseCIDR(parentNetConf["ipv6.address"]) + parentIPv6, parentIPv6Net, err := net.ParseCIDR(parentNetConf[v6CIDRKey]) if err == nil { v.dnsIPv6 = parentIPv6 v.routerExtGwIPv6 = parentIPv6 From b30f975a6838367a1e117b27583666781d8903f1 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 2 Oct 2020 15:33:29 +0100 Subject: [PATCH 04/13] lxd/network/driver/ovn: Better error messages Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index f4ab603101..ff185854a5 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -365,7 +365,7 @@ func (n *ovn) setupParentPort(routerMAC net.HardwareAddr) (*ovnParentVars, error return n.setupParentPortBridge(parentNet, routerMAC) } - return nil, fmt.Errorf("Network type %q unsupported as OVN parent", parentNet.Type()) + return nil, fmt.Errorf("Failed setting up parent port, network type %q unsupported as OVN parent", parentNet.Type()) } // setupParentPortBridge allocates external IPs on the parent bridge. @@ -597,7 +597,7 @@ func (n *ovn) startParentPort() error { return n.startParentPortBridge(parentNet) } - return fmt.Errorf("Network type %q unsupported as OVN parent", parentNet.Type()) + return fmt.Errorf("Failed starting parent port, network type %q unsupported as OVN parent", parentNet.Type()) } // parentOperationLockName returns the lock name to use for operations on the parent network. @@ -727,7 +727,7 @@ func (n *ovn) deleteParentPort() error { return n.deleteParentPortBridge(parentNet) } - return fmt.Errorf("Network type %q unsupported as OVN parent", parentNet.Type()) + return fmt.Errorf("Failed deleting parent port, network type %q unsupported as OVN parent", parentNet.Type()) } return nil From fdc703d3e3e83fea3c89da0c849b018ca7976da9 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 2 Oct 2020 15:33:46 +0100 Subject: [PATCH 05/13] lxd/network/driver/ovn: Moves parent port lock into deleteParentPort So it applies to all uplink parent network types. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index ff185854a5..d73e02f4d7 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -722,6 +722,10 @@ func (n *ovn) deleteParentPort() error { return errors.Wrapf(err, "Failed loading parent network") } + // Lock parent network so we don't race each other networks using the OVS uplink bridge. + unlock := locking.Lock(n.parentOperationLockName(parentNet)) + defer unlock() + switch parentNet.Type() { case "bridge": return n.deleteParentPortBridge(parentNet) @@ -735,10 +739,6 @@ func (n *ovn) deleteParentPort() error { // deleteParentPortBridge deletes the dnsmasq static lease and removes parent uplink OVS bridge if not in use. func (n *ovn) deleteParentPortBridge(parentNet Network) error { - // Lock parent network so we don't race each other networks using the OVS uplink bridge. - unlock := locking.Lock(n.parentOperationLockName(parentNet)) - defer unlock() - // Check OVS uplink bridge exists, if it does, check how many ports it has. removeVeths := false vars := n.parentPortBridgeVars(parentNet) From 2d4c4be6b06d519a89f09ef1fd88405da094c412 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 2 Oct 2020 16:28:54 +0100 Subject: [PATCH 06/13] lxd/network/driver/ovn: Moves parent port lock into startParentPort So it applies to all uplink parent network types. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index d73e02f4d7..c4981aa68f 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -592,6 +592,11 @@ func (n *ovn) startParentPort() error { return errors.Wrapf(err, "Failed loading parent network") } + // Lock parent network so that if multiple OVN networks are trying to connect to the same parent we don't + // race each other setting up the connection. + unlock := locking.Lock(n.parentOperationLockName(parentNet)) + defer unlock() + switch parentNet.Type() { case "bridge": return n.startParentPortBridge(parentNet) @@ -621,11 +626,6 @@ func (n *ovn) parentPortBridgeVars(parentNet Network) *ovnParentPortBridgeVars { func (n *ovn) startParentPortBridge(parentNet Network) error { vars := n.parentPortBridgeVars(parentNet) - // Lock parent network so that if multiple OVN networks are trying to connect to the same parent we don't - // race each other setting up the connection. - unlock := locking.Lock(n.parentOperationLockName(parentNet)) - defer unlock() - // Do this after gaining lock so that on failure we revert before release locking. revert := revert.New() defer revert.Fail() From 8e821a1af8fdd708cd353b11d4daeb2933be836c Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 2 Oct 2020 15:34:41 +0100 Subject: [PATCH 07/13] lxd/network/driver/ovn: deleteParentPortBridge comments Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index c4981aa68f..3e3c29c4bc 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -737,7 +737,7 @@ func (n *ovn) deleteParentPort() error { return nil } -// deleteParentPortBridge deletes the dnsmasq static lease and removes parent uplink OVS bridge if not in use. +// deleteParentPortBridge deletes parent uplink OVS bridge, OVN bridge mappings and veth interfaces if not in use. func (n *ovn) deleteParentPortBridge(parentNet Network) error { // Check OVS uplink bridge exists, if it does, check how many ports it has. removeVeths := false From e8f50946eeed770926ef7648d730943bf23b8406 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 2 Oct 2020 15:35:35 +0100 Subject: [PATCH 08/13] lxd/network/driver/ovn: Don't setup SNAT if no external uplink IPs Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 3e3c29c4bc..7d6909abab 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1052,14 +1052,14 @@ func (n *ovn) setup(update bool) error { } // Add SNAT rules. - if routerIntPortIPv4Net != nil { + if routerIntPortIPv4Net != nil && routerExtPortIPv4 != nil { err = client.LogicalRouterSNATAdd(n.getRouterName(), routerIntPortIPv4Net, routerExtPortIPv4) if err != nil { return err } } - if routerIntPortIPv6Net != nil { + if routerIntPortIPv6Net != nil && routerExtPortIPv6 != nil { err = client.LogicalRouterSNATAdd(n.getRouterName(), routerIntPortIPv6Net, routerExtPortIPv6) if err != nil { return err From 02cb55fc11e0d597d67a094858ccf9e478bb1988 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 2 Oct 2020 15:58:24 +0100 Subject: [PATCH 09/13] lxd/network/driver/ovn: Makes setting up external router port and switch conditional on having external IPs Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 84 ++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 7d6909abab..2c0a5ae001 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1066,17 +1066,6 @@ func (n *ovn) setup(update bool) error { } } - // Create external logical switch. - if update { - client.LogicalSwitchDelete(n.getExtSwitchName()) - } - - err = client.LogicalSwitchAdd(n.getExtSwitchName(), false) - if err != nil { - return errors.Wrapf(err, "Failed adding external switch") - } - revert.Add(func() { client.LogicalSwitchDelete(n.getExtSwitchName()) }) - // Generate external router port IPs (in CIDR format). extRouterIPs := []*net.IPNet{} if routerExtPortIPv4Net != nil { @@ -1093,41 +1082,54 @@ func (n *ovn) setup(update bool) error { }) } - // Create external router port. - err = client.LogicalRouterPortAdd(n.getRouterName(), n.getRouterExtPortName(), routerMAC, extRouterIPs...) - if err != nil { - return errors.Wrapf(err, "Failed adding external router port") - } - revert.Add(func() { client.LogicalRouterPortDelete(n.getRouterExtPortName()) }) + if len(extRouterIPs) > 0 { + // Create external logical switch. + if update { + client.LogicalSwitchDelete(n.getExtSwitchName()) + } - // Associate external router port to chassis group. - err = client.LogicalRouterPortLinkChassisGroup(n.getRouterExtPortName(), n.getChassisGroupName()) - if err != nil { - return errors.Wrapf(err, "Failed linking external router port to chassis group") - } + err = client.LogicalSwitchAdd(n.getExtSwitchName(), false) + if err != nil { + return errors.Wrapf(err, "Failed adding external switch") + } + revert.Add(func() { client.LogicalSwitchDelete(n.getExtSwitchName()) }) - // Create external switch port and link to router port. - err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), n.getExtSwitchRouterPortName(), false) - if err != nil { - return errors.Wrapf(err, "Failed adding external switch router port") - } - revert.Add(func() { client.LogicalSwitchPortDelete(n.getExtSwitchRouterPortName()) }) + // Create external router port. + err = client.LogicalRouterPortAdd(n.getRouterName(), n.getRouterExtPortName(), routerMAC, extRouterIPs...) + if err != nil { + return errors.Wrapf(err, "Failed adding external router port") + } + revert.Add(func() { client.LogicalRouterPortDelete(n.getRouterExtPortName()) }) - err = client.LogicalSwitchPortLinkRouter(n.getExtSwitchRouterPortName(), n.getRouterExtPortName()) - if err != nil { - return errors.Wrapf(err, "Failed linking external router port to external switch port") - } + // Associate external router port to chassis group. + err = client.LogicalRouterPortLinkChassisGroup(n.getRouterExtPortName(), n.getChassisGroupName()) + if err != nil { + return errors.Wrapf(err, "Failed linking external router port to chassis group") + } - // Create external switch port and link to external provider network. - err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), n.getExtSwitchProviderPortName(), false) - if err != nil { - return errors.Wrapf(err, "Failed adding external switch provider port") - } - revert.Add(func() { client.LogicalSwitchPortDelete(n.getExtSwitchProviderPortName()) }) + // Create external switch port and link to router port. + err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), n.getExtSwitchRouterPortName(), false) + if err != nil { + return errors.Wrapf(err, "Failed adding external switch router port") + } + revert.Add(func() { client.LogicalSwitchPortDelete(n.getExtSwitchRouterPortName()) }) - err = client.LogicalSwitchPortLinkProviderNetwork(n.getExtSwitchProviderPortName(), parent.extSwitchProviderName) - if err != nil { - return errors.Wrapf(err, "Failed linking external switch provider port to external provider network") + err = client.LogicalSwitchPortLinkRouter(n.getExtSwitchRouterPortName(), n.getRouterExtPortName()) + if err != nil { + return errors.Wrapf(err, "Failed linking external router port to external switch port") + } + + // Create external switch port and link to external provider network. + err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), n.getExtSwitchProviderPortName(), false) + if err != nil { + return errors.Wrapf(err, "Failed adding external switch provider port") + } + revert.Add(func() { client.LogicalSwitchPortDelete(n.getExtSwitchProviderPortName()) }) + + err = client.LogicalSwitchPortLinkProviderNetwork(n.getExtSwitchProviderPortName(), parent.extSwitchProviderName) + if err != nil { + return errors.Wrapf(err, "Failed linking external switch provider port to external provider network") + } } // Create internal logical switch if not updating. From 3099041494bec4a42bb53e75c5a85f30e50718f6 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 2 Oct 2020 15:58:44 +0100 Subject: [PATCH 10/13] lxd/network/driver/ovn: Removes old comment Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 2c0a5ae001..bc0404218e 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1149,8 +1149,6 @@ func (n *ovn) setup(update bool) error { return errors.Wrapf(err, "Failed setting IP allocation settings on internal switch") } - // Find MAC address for internal router port. - var dhcpv4UUID, dhcpv6UUID string if update { From e9fc96df5e28540c5a213e5895134d7589c26c9a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 5 Oct 2020 16:20:45 +0100 Subject: [PATCH 11/13] lxd/network/driver/ovn: Fix sentence in startParentPortBridge error Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index bc0404218e..6b98923523 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -648,7 +648,7 @@ func (n *ovn) startParentPortBridge(parentNet Network) error { fmt.Sprintf("net.ipv6.conf.%s.forwarding=0", vars.ovsEnd), ) if err != nil { - return errors.Wrapf(err, "Failed to set configure uplink veth interfaces %q and %q", vars.parentEnd, vars.ovsEnd) + return errors.Wrapf(err, "Failed to configure uplink veth interfaces %q and %q", vars.parentEnd, vars.ovsEnd) } // Connect parent end of veth pair to parent bridge and bring up. From 137fcbb8b0813eba045c8f2972b2df465a9b9735 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 09:58:58 +0100 Subject: [PATCH 12/13] lxd/network/driver/ovn: Fixes error message in setupParentPortBridge Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 6b98923523..99423d4b3c 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -383,7 +383,7 @@ func (n *ovn) setupParentPortBridge(parentNet Network, routerMAC net.HardwareAdd v, err := n.allocateParentPortIPs(parentNet, "ipv4.address", "ipv6.address", routerMAC) if err != nil { - return nil, errors.Wrapf(err, "Failed allocating parent port IPs on network", parentNet.Name()) + return nil, errors.Wrapf(err, "Failed allocating parent port IPs on network %q", parentNet.Name()) } return v, nil From df0a848f3d9c889b7bd72e1401c850a1584a711a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 10:27:37 +0100 Subject: [PATCH 13/13] lxd/network/network/utils: Moves bridge related functions into own file Signed-off-by: Thomas Parrott --- lxd/network/network_utils.go | 82 ------------------------- lxd/network/network_utils_bridge.go | 93 +++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 82 deletions(-) create mode 100644 lxd/network/network_utils_bridge.go diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go index 4723340161..010a00978c 100644 --- a/lxd/network/network_utils.go +++ b/lxd/network/network_utils.go @@ -24,7 +24,6 @@ import ( "github.com/lxc/lxd/lxd/dnsmasq/dhcpalloc" "github.com/lxc/lxd/lxd/instance" "github.com/lxc/lxd/lxd/instance/instancetype" - "github.com/lxc/lxd/lxd/network/openvswitch" "github.com/lxc/lxd/lxd/project" "github.com/lxc/lxd/lxd/state" "github.com/lxc/lxd/shared" @@ -250,47 +249,6 @@ func isInUseByDevices(s *state.State, networkProjectName string, networkName str return false, nil } -// IsNativeBridge returns whether the bridge name specified is a Linux native bridge. -func IsNativeBridge(bridgeName string) bool { - return shared.PathExists(fmt.Sprintf("/sys/class/net/%s/bridge", bridgeName)) -} - -// AttachInterface attaches an interface to a bridge. -func AttachInterface(bridgeName string, devName string) error { - if IsNativeBridge(bridgeName) { - _, err := shared.RunCommand("ip", "link", "set", "dev", devName, "master", bridgeName) - if err != nil { - return err - } - } else { - ovs := openvswitch.NewOVS() - err := ovs.BridgePortAdd(bridgeName, devName, true) - if err != nil { - return err - } - } - - return nil -} - -// DetachInterface detaches an interface from a bridge. -func DetachInterface(bridgeName string, devName string) error { - if IsNativeBridge(bridgeName) { - _, err := shared.RunCommand("ip", "link", "set", "dev", devName, "nomaster") - if err != nil { - return err - } - } else { - ovs := openvswitch.NewOVS() - err := ovs.BridgePortDelete(bridgeName, devName) - if err != nil { - return err - } - } - - return nil -} - // GetDevMTU retrieves the current MTU setting for a named network device. func GetDevMTU(devName string) (uint32, error) { content, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/mtu", devName)) @@ -932,46 +890,6 @@ func usesIPv6Firewall(netConfig map[string]string) bool { return false } -// BridgeVLANFilteringStatus returns whether VLAN filtering is enabled on a bridge interface. -func BridgeVLANFilteringStatus(interfaceName string) (string, error) { - content, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/bridge/vlan_filtering", interfaceName)) - if err != nil { - return "", errors.Wrapf(err, "Failed getting bridge VLAN status for %q", interfaceName) - } - - return strings.TrimSpace(fmt.Sprintf("%s", content)), nil -} - -// BridgeVLANFilterSetStatus sets the status of VLAN filtering on a bridge interface. -func BridgeVLANFilterSetStatus(interfaceName string, status string) error { - err := ioutil.WriteFile(fmt.Sprintf("/sys/class/net/%s/bridge/vlan_filtering", interfaceName), []byte(status), 0) - if err != nil { - return errors.Wrapf(err, "Failed enabling VLAN filtering on bridge %q", interfaceName) - } - - return nil -} - -// BridgeVLANDefaultPVID returns the VLAN default port VLAN ID (PVID). -func BridgeVLANDefaultPVID(interfaceName string) (string, error) { - content, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/bridge/default_pvid", interfaceName)) - if err != nil { - return "", errors.Wrapf(err, "Failed getting bridge VLAN default PVID for %q", interfaceName) - } - - return strings.TrimSpace(fmt.Sprintf("%s", content)), nil -} - -// BridgeVLANSetDefaultPVID sets the VLAN default port VLAN ID (PVID). -func BridgeVLANSetDefaultPVID(interfaceName string, vlanID string) error { - err := ioutil.WriteFile(fmt.Sprintf("/sys/class/net/%s/bridge/default_pvid", interfaceName), []byte(vlanID), 0) - if err != nil { - return errors.Wrapf(err, "Failed setting bridge VLAN default PVID for %q", interfaceName) - } - - return nil -} - // RandomHwaddr generates a random MAC address from the provided random source. func randomHwaddr(r *rand.Rand) string { // Generate a new random MAC address using the usual prefix. diff --git a/lxd/network/network_utils_bridge.go b/lxd/network/network_utils_bridge.go new file mode 100644 index 0000000000..f19b313e8d --- /dev/null +++ b/lxd/network/network_utils_bridge.go @@ -0,0 +1,93 @@ +package network + +import ( + "fmt" + "io/ioutil" + "strings" + + "github.com/pkg/errors" + + "github.com/lxc/lxd/lxd/network/openvswitch" + "github.com/lxc/lxd/shared" +) + +// BridgeVLANFilteringStatus returns whether VLAN filtering is enabled on a bridge interface. +func BridgeVLANFilteringStatus(interfaceName string) (string, error) { + content, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/bridge/vlan_filtering", interfaceName)) + if err != nil { + return "", errors.Wrapf(err, "Failed getting bridge VLAN status for %q", interfaceName) + } + + return strings.TrimSpace(fmt.Sprintf("%s", content)), nil +} + +// BridgeVLANFilterSetStatus sets the status of VLAN filtering on a bridge interface. +func BridgeVLANFilterSetStatus(interfaceName string, status string) error { + err := ioutil.WriteFile(fmt.Sprintf("/sys/class/net/%s/bridge/vlan_filtering", interfaceName), []byte(status), 0) + if err != nil { + return errors.Wrapf(err, "Failed enabling VLAN filtering on bridge %q", interfaceName) + } + + return nil +} + +// BridgeVLANDefaultPVID returns the VLAN default port VLAN ID (PVID). +func BridgeVLANDefaultPVID(interfaceName string) (string, error) { + content, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/bridge/default_pvid", interfaceName)) + if err != nil { + return "", errors.Wrapf(err, "Failed getting bridge VLAN default PVID for %q", interfaceName) + } + + return strings.TrimSpace(fmt.Sprintf("%s", content)), nil +} + +// BridgeVLANSetDefaultPVID sets the VLAN default port VLAN ID (PVID). +func BridgeVLANSetDefaultPVID(interfaceName string, vlanID string) error { + err := ioutil.WriteFile(fmt.Sprintf("/sys/class/net/%s/bridge/default_pvid", interfaceName), []byte(vlanID), 0) + if err != nil { + return errors.Wrapf(err, "Failed setting bridge VLAN default PVID for %q", interfaceName) + } + + return nil +} + +// IsNativeBridge returns whether the bridge name specified is a Linux native bridge. +func IsNativeBridge(bridgeName string) bool { + return shared.PathExists(fmt.Sprintf("/sys/class/net/%s/bridge", bridgeName)) +} + +// AttachInterface attaches an interface to a bridge. +func AttachInterface(bridgeName string, devName string) error { + if IsNativeBridge(bridgeName) { + _, err := shared.RunCommand("ip", "link", "set", "dev", devName, "master", bridgeName) + if err != nil { + return err + } + } else { + ovs := openvswitch.NewOVS() + err := ovs.BridgePortAdd(bridgeName, devName, true) + if err != nil { + return err + } + } + + return nil +} + +// DetachInterface detaches an interface from a bridge. +func DetachInterface(bridgeName string, devName string) error { + if IsNativeBridge(bridgeName) { + _, err := shared.RunCommand("ip", "link", "set", "dev", devName, "nomaster") + if err != nil { + return err + } + } else { + ovs := openvswitch.NewOVS() + err := ovs.BridgePortDelete(bridgeName, devName) + if err != nil { + return err + } + } + + return nil +} From lxc-bot at linuxcontainers.org Tue Oct 6 15:56:15 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Tue, 06 Oct 2020 08:56:15 -0700 (PDT) Subject: [lxc-devel] [lxd/master] network: Adds support for multiple upstream DNS servers in OVN networks Message-ID: <5f7c939f.1c69fb81.ee33f.0ceaSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 626 bytes Desc: not available URL: -------------- next part -------------- From efffd128341001511c5b3bbeb82af6178c71fd3d Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:31:37 +0100 Subject: [PATCH 1/5] shares/validate: Whitespace Signed-off-by: Thomas Parrott --- shared/validate/validate.go | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/validate/validate.go b/shared/validate/validate.go index 356a7d8744..4217ecf89f 100644 --- a/shared/validate/validate.go +++ b/shared/validate/validate.go @@ -226,6 +226,7 @@ func IsNetworkAddressV4List(value string) error { return err } } + return nil } From 896679be6effa830345a836920db173bd3935210 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:33:00 +0100 Subject: [PATCH 2/5] lxd/network/openvswitch/ovn: Updates RecursiveDNSServer to be list of IPs Signed-off-by: Thomas Parrott --- lxd/network/openvswitch/ovn.go | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go index ef181d8706..e0a0d0dc44 100644 --- a/lxd/network/openvswitch/ovn.go +++ b/lxd/network/openvswitch/ovn.go @@ -66,7 +66,7 @@ type OVNDHCPv4Opts struct { ServerID net.IP ServerMAC net.HardwareAddr Router net.IP - RecursiveDNSServer net.IP + RecursiveDNSServer []net.IP DomainName string LeaseTime time.Duration MTU uint32 @@ -75,7 +75,7 @@ type OVNDHCPv4Opts struct { // OVNDHCPv6Opts IPv6 DHCP option set that can be created (and then applied to a switch port by resulting ID). type OVNDHCPv6Opts struct { ServerID net.HardwareAddr - RecursiveDNSServer net.IP + RecursiveDNSServer []net.IP DNSSearchList []string } @@ -358,7 +358,16 @@ func (o *OVN) LogicalSwitchDHCPv4OptionsSet(switchName OVNSwitch, uuid string, s } if opts.RecursiveDNSServer != nil { - args = append(args, fmt.Sprintf("dns_server=%s", opts.RecursiveDNSServer.String())) + nsIPs := make([]string, 0, len(opts.RecursiveDNSServer)) + for _, nsIP := range opts.RecursiveDNSServer { + if nsIP.To4() == nil { + continue // Only include IPv4 addresses. + } + + nsIPs = append(nsIPs, nsIP.String()) + } + + args = append(args, fmt.Sprintf("dns_server={%s}", strings.Join(nsIPs, ","))) } if opts.DomainName != "" { @@ -416,7 +425,16 @@ func (o *OVN) LogicalSwitchDHCPv6OptionsSet(switchName OVNSwitch, uuid string, s } if opts.RecursiveDNSServer != nil { - args = append(args, fmt.Sprintf("dns_server=%s", opts.RecursiveDNSServer.String())) + nsIPs := make([]string, 0, len(opts.RecursiveDNSServer)) + for _, nsIP := range opts.RecursiveDNSServer { + if nsIP.To4() != nil { + continue // Only include IPv6 addresses. + } + + nsIPs = append(nsIPs, nsIP.String()) + } + + args = append(args, fmt.Sprintf("dns_server={%s}", strings.Join(nsIPs, ","))) } _, err = o.nbctl(args...) From c775eceac72df08927c5f6799e9b88244b19ea9f Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:35:07 +0100 Subject: [PATCH 3/5] lxd/network/driver/ovn: Updates allocateParentPortIPs to detect the parent network IP address and DNS settings Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 46 +++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 99423d4b3c..3080c9da3f 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -44,8 +44,8 @@ type ovnParentVars struct { extSwitchProviderName string // DNS. - dnsIPv6 net.IP - dnsIPv4 net.IP + dnsIPv6 []net.IP + dnsIPv4 []net.IP } // ovnParentPortBridgeVars parent bridge port variables used for start/stop. @@ -391,7 +391,7 @@ func (n *ovn) setupParentPortBridge(parentNet Network, routerMAC net.HardwareAdd // allocateParentPortIPs attempts to find a free IP in the parent network's OVN ranges and then stores it in // ovnVolatileParentIPv4 and ovnVolatileParentIPv6 config keys on this network. Returns ovnParentVars settings. -func (n *ovn) allocateParentPortIPs(parentNet Network, v4CIDRKey string, v6CIDRKey string, routerMAC net.HardwareAddr) (*ovnParentVars, error) { +func (n *ovn) allocateParentPortIPs(parentNet Network, routerMAC net.HardwareAddr) (*ovnParentVars, error) { v := &ovnParentVars{} parentNetConf := parentNet.Config() @@ -399,19 +399,51 @@ func (n *ovn) allocateParentPortIPs(parentNet Network, v4CIDRKey string, v6CIDRK // Parent derived settings. v.extSwitchProviderName = parentNet.Name() + // Detect parent gateway setting. + parentIPv4CIDR := parentNetConf["ipv4.address"] + if parentIPv4CIDR == "" { + parentIPv4CIDR = parentNetConf["ipv4.gateway"] + } + + parentIPv6CIDR := parentNetConf["ipv6.address"] + if parentIPv6CIDR == "" { + parentIPv6CIDR = parentNetConf["ipv6.gateway"] + } + // Optional parent values. - parentIPv4, parentIPv4Net, err := net.ParseCIDR(parentNetConf[v4CIDRKey]) + parentIPv4, parentIPv4Net, err := net.ParseCIDR(parentIPv4CIDR) if err == nil { - v.dnsIPv4 = parentIPv4 + v.dnsIPv4 = []net.IP{parentIPv4} v.routerExtGwIPv4 = parentIPv4 } - parentIPv6, parentIPv6Net, err := net.ParseCIDR(parentNetConf[v6CIDRKey]) + parentIPv6, parentIPv6Net, err := net.ParseCIDR(parentIPv6CIDR) if err == nil { - v.dnsIPv6 = parentIPv6 + v.dnsIPv6 = []net.IP{parentIPv6} v.routerExtGwIPv6 = parentIPv6 } + // Detect optional DNS server list. + if parentNetConf["dns.nameservers"] != "" { + // Reset nameservers. + v.dnsIPv4 = nil + v.dnsIPv6 = nil + + nsList := strings.Split(parentNetConf["dns.nameservers"], ",") + for _, ns := range nsList { + nsIP := net.ParseIP(strings.TrimSpace(ns)) + if nsIP == nil { + return nil, fmt.Errorf("Invalid parent nameserver") + } + + if nsIP.To4() == nil { + v.dnsIPv6 = append(v.dnsIPv6, nsIP) + } else { + v.dnsIPv4 = append(v.dnsIPv4, nsIP) + } + } + } + // Parse existing allocated IPs for this network on the parent network (if not set yet, will be nil). routerExtPortIPv4 := net.ParseIP(n.config[ovnVolatileParentIPv4]) routerExtPortIPv6 := net.ParseIP(n.config[ovnVolatileParentIPv6]) From 6c99bdfd32f4cce8b6cb3032eda69113ee8b0b92 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:36:54 +0100 Subject: [PATCH 4/5] lxd/network/driver/ovn: Updates n.allocateParentPortIPs usage Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 3080c9da3f..4af51c3092 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -381,7 +381,7 @@ func (n *ovn) setupParentPortBridge(parentNet Network, routerMAC net.HardwareAdd return nil, errors.Wrapf(err, "Network %q is not suitable for use as OVN parent", bridgeNet.name) } - v, err := n.allocateParentPortIPs(parentNet, "ipv4.address", "ipv6.address", routerMAC) + v, err := n.allocateParentPortIPs(parentNet, routerMAC) if err != nil { return nil, errors.Wrapf(err, "Failed allocating parent port IPs on network %q", parentNet.Name()) } From 11a0289883dbeb856344a803535cafb169e8edb3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:37:32 +0100 Subject: [PATCH 5/5] lxd/network/driver/ovn: Updates setup IPv6 RDNSS setting Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 4af51c3092..445e555433 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1257,11 +1257,16 @@ func (n *ovn) setup(update bool) error { adressMode = openvswitch.OVNIPv6AddressModeDHCPStateful } + var recursiveDNSServer net.IP + if len(parent.dnsIPv6) > 0 { + recursiveDNSServer = parent.dnsIPv6[0] // OVN only supports 1 RA DNS server. + } + err = client.LogicalRouterPortSetIPv6Advertisements(n.getRouterIntPortName(), &openvswitch.OVNIPv6RAOpts{ AddressMode: adressMode, SendPeriodic: true, DNSSearchList: n.getDNSSearchList(), - RecursiveDNSServer: parent.dnsIPv6, + RecursiveDNSServer: recursiveDNSServer, MTU: bridgeMTU, // Keep these low until we support DNS search domains via DHCPv4, as otherwise RA DNSSL From lxc-bot at linuxcontainers.org Tue Oct 6 17:15:49 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Tue, 06 Oct 2020 10:15:49 -0700 (PDT) Subject: [lxc-devel] [lxd/master] network: Adds physical network type and adds OVN support for using it as an uplink Message-ID: <5f7ca645.1c69fb81.9dd36.eda6SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 470 bytes Desc: not available URL: -------------- next part -------------- From efffd128341001511c5b3bbeb82af6178c71fd3d Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:31:37 +0100 Subject: [PATCH 01/14] shares/validate: Whitespace Signed-off-by: Thomas Parrott --- shared/validate/validate.go | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/validate/validate.go b/shared/validate/validate.go index 356a7d8744..4217ecf89f 100644 --- a/shared/validate/validate.go +++ b/shared/validate/validate.go @@ -226,6 +226,7 @@ func IsNetworkAddressV4List(value string) error { return err } } + return nil } From 896679be6effa830345a836920db173bd3935210 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:33:00 +0100 Subject: [PATCH 02/14] lxd/network/openvswitch/ovn: Updates RecursiveDNSServer to be list of IPs Signed-off-by: Thomas Parrott --- lxd/network/openvswitch/ovn.go | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go index ef181d8706..e0a0d0dc44 100644 --- a/lxd/network/openvswitch/ovn.go +++ b/lxd/network/openvswitch/ovn.go @@ -66,7 +66,7 @@ type OVNDHCPv4Opts struct { ServerID net.IP ServerMAC net.HardwareAddr Router net.IP - RecursiveDNSServer net.IP + RecursiveDNSServer []net.IP DomainName string LeaseTime time.Duration MTU uint32 @@ -75,7 +75,7 @@ type OVNDHCPv4Opts struct { // OVNDHCPv6Opts IPv6 DHCP option set that can be created (and then applied to a switch port by resulting ID). type OVNDHCPv6Opts struct { ServerID net.HardwareAddr - RecursiveDNSServer net.IP + RecursiveDNSServer []net.IP DNSSearchList []string } @@ -358,7 +358,16 @@ func (o *OVN) LogicalSwitchDHCPv4OptionsSet(switchName OVNSwitch, uuid string, s } if opts.RecursiveDNSServer != nil { - args = append(args, fmt.Sprintf("dns_server=%s", opts.RecursiveDNSServer.String())) + nsIPs := make([]string, 0, len(opts.RecursiveDNSServer)) + for _, nsIP := range opts.RecursiveDNSServer { + if nsIP.To4() == nil { + continue // Only include IPv4 addresses. + } + + nsIPs = append(nsIPs, nsIP.String()) + } + + args = append(args, fmt.Sprintf("dns_server={%s}", strings.Join(nsIPs, ","))) } if opts.DomainName != "" { @@ -416,7 +425,16 @@ func (o *OVN) LogicalSwitchDHCPv6OptionsSet(switchName OVNSwitch, uuid string, s } if opts.RecursiveDNSServer != nil { - args = append(args, fmt.Sprintf("dns_server=%s", opts.RecursiveDNSServer.String())) + nsIPs := make([]string, 0, len(opts.RecursiveDNSServer)) + for _, nsIP := range opts.RecursiveDNSServer { + if nsIP.To4() != nil { + continue // Only include IPv6 addresses. + } + + nsIPs = append(nsIPs, nsIP.String()) + } + + args = append(args, fmt.Sprintf("dns_server={%s}", strings.Join(nsIPs, ","))) } _, err = o.nbctl(args...) From c775eceac72df08927c5f6799e9b88244b19ea9f Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:35:07 +0100 Subject: [PATCH 03/14] lxd/network/driver/ovn: Updates allocateParentPortIPs to detect the parent network IP address and DNS settings Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 46 +++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 99423d4b3c..3080c9da3f 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -44,8 +44,8 @@ type ovnParentVars struct { extSwitchProviderName string // DNS. - dnsIPv6 net.IP - dnsIPv4 net.IP + dnsIPv6 []net.IP + dnsIPv4 []net.IP } // ovnParentPortBridgeVars parent bridge port variables used for start/stop. @@ -391,7 +391,7 @@ func (n *ovn) setupParentPortBridge(parentNet Network, routerMAC net.HardwareAdd // allocateParentPortIPs attempts to find a free IP in the parent network's OVN ranges and then stores it in // ovnVolatileParentIPv4 and ovnVolatileParentIPv6 config keys on this network. Returns ovnParentVars settings. -func (n *ovn) allocateParentPortIPs(parentNet Network, v4CIDRKey string, v6CIDRKey string, routerMAC net.HardwareAddr) (*ovnParentVars, error) { +func (n *ovn) allocateParentPortIPs(parentNet Network, routerMAC net.HardwareAddr) (*ovnParentVars, error) { v := &ovnParentVars{} parentNetConf := parentNet.Config() @@ -399,19 +399,51 @@ func (n *ovn) allocateParentPortIPs(parentNet Network, v4CIDRKey string, v6CIDRK // Parent derived settings. v.extSwitchProviderName = parentNet.Name() + // Detect parent gateway setting. + parentIPv4CIDR := parentNetConf["ipv4.address"] + if parentIPv4CIDR == "" { + parentIPv4CIDR = parentNetConf["ipv4.gateway"] + } + + parentIPv6CIDR := parentNetConf["ipv6.address"] + if parentIPv6CIDR == "" { + parentIPv6CIDR = parentNetConf["ipv6.gateway"] + } + // Optional parent values. - parentIPv4, parentIPv4Net, err := net.ParseCIDR(parentNetConf[v4CIDRKey]) + parentIPv4, parentIPv4Net, err := net.ParseCIDR(parentIPv4CIDR) if err == nil { - v.dnsIPv4 = parentIPv4 + v.dnsIPv4 = []net.IP{parentIPv4} v.routerExtGwIPv4 = parentIPv4 } - parentIPv6, parentIPv6Net, err := net.ParseCIDR(parentNetConf[v6CIDRKey]) + parentIPv6, parentIPv6Net, err := net.ParseCIDR(parentIPv6CIDR) if err == nil { - v.dnsIPv6 = parentIPv6 + v.dnsIPv6 = []net.IP{parentIPv6} v.routerExtGwIPv6 = parentIPv6 } + // Detect optional DNS server list. + if parentNetConf["dns.nameservers"] != "" { + // Reset nameservers. + v.dnsIPv4 = nil + v.dnsIPv6 = nil + + nsList := strings.Split(parentNetConf["dns.nameservers"], ",") + for _, ns := range nsList { + nsIP := net.ParseIP(strings.TrimSpace(ns)) + if nsIP == nil { + return nil, fmt.Errorf("Invalid parent nameserver") + } + + if nsIP.To4() == nil { + v.dnsIPv6 = append(v.dnsIPv6, nsIP) + } else { + v.dnsIPv4 = append(v.dnsIPv4, nsIP) + } + } + } + // Parse existing allocated IPs for this network on the parent network (if not set yet, will be nil). routerExtPortIPv4 := net.ParseIP(n.config[ovnVolatileParentIPv4]) routerExtPortIPv6 := net.ParseIP(n.config[ovnVolatileParentIPv6]) From 6c99bdfd32f4cce8b6cb3032eda69113ee8b0b92 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:36:54 +0100 Subject: [PATCH 04/14] lxd/network/driver/ovn: Updates n.allocateParentPortIPs usage Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 3080c9da3f..4af51c3092 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -381,7 +381,7 @@ func (n *ovn) setupParentPortBridge(parentNet Network, routerMAC net.HardwareAdd return nil, errors.Wrapf(err, "Network %q is not suitable for use as OVN parent", bridgeNet.name) } - v, err := n.allocateParentPortIPs(parentNet, "ipv4.address", "ipv6.address", routerMAC) + v, err := n.allocateParentPortIPs(parentNet, routerMAC) if err != nil { return nil, errors.Wrapf(err, "Failed allocating parent port IPs on network %q", parentNet.Name()) } From 11a0289883dbeb856344a803535cafb169e8edb3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:37:32 +0100 Subject: [PATCH 05/14] lxd/network/driver/ovn: Updates setup IPv6 RDNSS setting Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 4af51c3092..445e555433 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1257,11 +1257,16 @@ func (n *ovn) setup(update bool) error { adressMode = openvswitch.OVNIPv6AddressModeDHCPStateful } + var recursiveDNSServer net.IP + if len(parent.dnsIPv6) > 0 { + recursiveDNSServer = parent.dnsIPv6[0] // OVN only supports 1 RA DNS server. + } + err = client.LogicalRouterPortSetIPv6Advertisements(n.getRouterIntPortName(), &openvswitch.OVNIPv6RAOpts{ AddressMode: adressMode, SendPeriodic: true, DNSSearchList: n.getDNSSearchList(), - RecursiveDNSServer: parent.dnsIPv6, + RecursiveDNSServer: recursiveDNSServer, MTU: bridgeMTU, // Keep these low until we support DNS search domains via DHCPv4, as otherwise RA DNSSL From e61c4857d7cf2f753fd51880ba00a237809c4a04 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:31:26 +0100 Subject: [PATCH 06/14] shared/validate: Adds IsNetworkAddressList function Signed-off-by: Thomas Parrott --- shared/validate/validate.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/shared/validate/validate.go b/shared/validate/validate.go index 4217ecf89f..10067d8c26 100644 --- a/shared/validate/validate.go +++ b/shared/validate/validate.go @@ -171,6 +171,19 @@ func IsNetworkAddress(value string) error { return nil } +// IsNetworkAddressList validates a comma delimited list of IPv4 or IPv6 addresses. +func IsNetworkAddressList(value string) error { + for _, v := range strings.Split(value, ",") { + v = strings.TrimSpace(v) + err := IsNetworkAddress(v) + if err != nil { + return err + } + } + + return nil +} + // IsNetworkV4 validates an IPv4 CIDR string. If string is empty, returns valid. func IsNetworkV4(value string) error { ip, subnet, err := net.ParseCIDR(value) From c8fa53c40537fc1b39c1e1e138474166146b669f Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:31:54 +0100 Subject: [PATCH 07/14] lxd/network/network/physical: Adds physical driver Signed-off-by: Thomas Parrott --- lxd/network/network_load.go | 9 +- lxd/network/network_physical.go | 141 ++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 lxd/network/network_physical.go diff --git a/lxd/network/network_load.go b/lxd/network/network_load.go index cf59074663..7098181a57 100644 --- a/lxd/network/network_load.go +++ b/lxd/network/network_load.go @@ -5,10 +5,11 @@ import ( ) var drivers = map[string]func() Network{ - "bridge": func() Network { return &bridge{} }, - "macvlan": func() Network { return &macvlan{} }, - "sriov": func() Network { return &sriov{} }, - "ovn": func() Network { return &ovn{} }, + "bridge": func() Network { return &bridge{} }, + "macvlan": func() Network { return &macvlan{} }, + "sriov": func() Network { return &sriov{} }, + "ovn": func() Network { return &ovn{} }, + "physical": func() Network { return &physical{} }, } // LoadByType loads a network by driver type. diff --git a/lxd/network/network_physical.go b/lxd/network/network_physical.go new file mode 100644 index 0000000000..2ea04f7ed4 --- /dev/null +++ b/lxd/network/network_physical.go @@ -0,0 +1,141 @@ +package network + +import ( + "fmt" + "net" + + "github.com/lxc/lxd/lxd/cluster" + "github.com/lxc/lxd/lxd/db" + "github.com/lxc/lxd/lxd/revert" + "github.com/lxc/lxd/shared/api" + log "github.com/lxc/lxd/shared/log15" + "github.com/lxc/lxd/shared/validate" +) + +// physical represents a LXD physical network. +type physical struct { + common +} + +// Type returns the network type. +func (n *physical) Type() string { + return "physical" +} + +// DBType returns the network type DB ID. +func (n *physical) DBType() db.NetworkType { + return db.NetworkTypePhysical +} + +// Validate network config. +func (n *physical) Validate(config map[string]string) error { + rules := map[string]func(value string) error{ + "parent": validate.Required(validate.IsNotEmpty, validInterfaceName), + "mtu": validate.Optional(validate.IsNetworkMTU), + "vlan": validate.Optional(validate.IsNetworkVLAN), + "maas.subnet.ipv4": validate.IsAny, + "maas.subnet.ipv6": validate.IsAny, + "ipv4.gateway": validate.Optional(validate.IsNetworkAddressCIDRV4), + "ipv6.gateway": validate.Optional(validate.IsNetworkAddressCIDRV6), + "ipv4.ovn.ranges": validate.Optional(validate.IsNetworkRangeV4List), + "ipv6.ovn.ranges": validate.Optional(validate.IsNetworkRangeV6List), + "dns.nameservers": validate.Optional(validate.IsNetworkAddressList), + } + + err := n.validate(config, rules) + if err != nil { + return err + } + + return nil +} + +// Delete deletes a network. +func (n *physical) Delete(clientType cluster.ClientType) error { + n.logger.Debug("Delete", log.Ctx{"clientType": clientType}) + return n.common.delete(clientType) +} + +// Rename renames a network. +func (n *physical) Rename(newName string) error { + n.logger.Debug("Rename", log.Ctx{"newName": newName}) + + // Rename common steps. + err := n.common.rename(newName) + if err != nil { + return err + } + + return nil +} + +// Start starts is a no-op. +func (n *physical) Start() error { + n.logger.Debug("Start") + + if n.status == api.NetworkStatusPending { + return fmt.Errorf("Cannot start pending network") + } + + return nil +} + +// Stop stops is a no-op. +func (n *physical) Stop() error { + n.logger.Debug("Stop") + + return nil +} + +// Update updates the network. Accepts notification boolean indicating if this update request is coming from a +// cluster notification, in which case do not update the database, just apply local changes needed. +func (n *physical) Update(newNetwork api.NetworkPut, targetNode string, clientType cluster.ClientType) error { + n.logger.Debug("Update", log.Ctx{"clientType": clientType, "newNetwork": newNetwork}) + + dbUpdateNeeeded, _, oldNetwork, err := n.common.configChanged(newNetwork) + if err != nil { + return err + } + + if !dbUpdateNeeeded { + return nil // Nothing changed. + } + + revert := revert.New() + defer revert.Fail() + + // Define a function which reverts everything. + revert.Add(func() { + // Reset changes to all nodes and database. + n.common.update(oldNetwork, targetNode, clientType) + }) + + // Apply changes to database. + err = n.common.update(newNetwork, targetNode, clientType) + if err != nil { + return err + } + + revert.Success() + return nil +} + +// DHCPv4Subnet returns the DHCPv4 subnet (if DHCP is enabled on network). +func (n *physical) DHCPv4Subnet() *net.IPNet { + _, subnet, err := net.ParseCIDR(n.config["ipv4.gateway"]) + if err != nil { + return nil + } + + return subnet +} + +// DHCPv6Subnet returns the DHCPv6 subnet (if DHCP or SLAAC is enabled on network). +func (n *physical) DHCPv6Subnet() *net.IPNet { + _, subnet, err := net.ParseCIDR(n.config["ipv6.gateway"]) + if err != nil { + return nil + } + + return subnet +} From c6efa4013d6710a3cf83bf15e12f0e36c43cb00c Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:32:25 +0100 Subject: [PATCH 08/14] lxd/db/networks: Adds physical network type constant Signed-off-by: Thomas Parrott --- lxd/db/networks.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lxd/db/networks.go b/lxd/db/networks.go index 27eb62964d..3b10447bde 100644 --- a/lxd/db/networks.go +++ b/lxd/db/networks.go @@ -425,10 +425,11 @@ type NetworkType int // Network types. const ( - NetworkTypeBridge NetworkType = iota // Network type bridge. - NetworkTypeMacvlan // Network type macvlan. - NetworkTypeSriov // Network type sriov. - NetworkTypeOVN // Network type ovn. + NetworkTypeBridge NetworkType = iota // Network type bridge. + NetworkTypeMacvlan // Network type macvlan. + NetworkTypeSriov // Network type sriov. + NetworkTypeOVN // Network type ovn. + NetworkTypePhysical // Network type physical. ) // GetNetworkInAnyState returns the network with the given name. @@ -510,6 +511,8 @@ func networkFillType(network *api.Network, netType NetworkType) { network.Type = "sriov" case NetworkTypeOVN: network.Type = "ovn" + case NetworkTypePhysical: + network.Type = "physical" default: network.Type = "" // Unknown } From 5578e5c1a5aea1d218de1af55b9b26755e91ce54 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 17:24:51 +0100 Subject: [PATCH 09/14] api: Adds network_type_physical extension Signed-off-by: Thomas Parrott --- doc/api-extensions.md | 7 ++++++- shared/version/api.go | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index d7c233c3e6..bc3ee9e2d2 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -1185,4 +1185,9 @@ when restoring a custom volume backup. ## storage\_rsync\_compression Adds `rsync.compression` config key to storage pools. This key can be used -to disable compression in rsync while migrating storage pools. \ No newline at end of file +to disable compression in rsync while migrating storage pools. + +## network\_type\_physical +Adds support for additional network type `physical` that can be used as an uplink for `ovn` networks. + +The interface specified by `parent` on the `physical` network will be connected to the `ovn` network's gateway. diff --git a/shared/version/api.go b/shared/version/api.go index ba9380b676..d5035f7161 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -229,6 +229,7 @@ var APIExtensions = []string{ "custom_volume_backup", "backup_override_name", "storage_rsync_compression", + "network_type_physical", } // APIExtensionsCount returns the number of available API extensions. From d2fee1c90a17d64859e33363645420c219eb4421 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 18:09:24 +0100 Subject: [PATCH 10/14] lxd/network/network/utils: Adds VLANInterfaceCreate function Signed-off-by: Thomas Parrott --- lxd/network/network_utils.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go index 010a00978c..c9b97be607 100644 --- a/lxd/network/network_utils.go +++ b/lxd/network/network_utils.go @@ -26,6 +26,7 @@ import ( "github.com/lxc/lxd/lxd/instance/instancetype" "github.com/lxc/lxd/lxd/project" "github.com/lxc/lxd/lxd/state" + "github.com/lxc/lxd/lxd/util" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" "github.com/lxc/lxd/shared/logger" @@ -1014,3 +1015,33 @@ func parseIPRanges(ipRangesList string, allowedNets ...*net.IPNet) ([]*shared.IP return netIPRanges, nil } + +// VLANInterfaceCreate creates a VLAN interface on parent interface (if needed). +// Returns boolean indicating if VLAN interface was created. +func VLANInterfaceCreate(parent string, vlanDevice string, vlanID string) (bool, error) { + if vlanID == "" { + return false, nil + } + + if shared.PathExists(fmt.Sprintf("/sys/class/net/%s", vlanDevice)) { + return false, nil + } + + // Bring the parent interface up so we can add a vlan to it. + _, err := shared.RunCommand("ip", "link", "set", "dev", parent, "up") + if err != nil { + return false, errors.Wrapf(err, "Failed to bring up parent %q", parent) + } + + // Add VLAN interface on top of parent. + _, err = shared.RunCommand("ip", "link", "add", "link", parent, "name", vlanDevice, "up", "type", "vlan", "id", vlanID) + if err != nil { + return false, errors.Wrapf(err, "Failed to create VLAN interface %q on %q", vlanDevice, parent) + } + + // Attempt to disable IPv6 router advertisement acceptance. + util.SysctlSet(fmt.Sprintf("net/ipv6/conf/%s/accept_ra", vlanDevice), "0") + + // We created a new vlan interface, return true. + return true, nil +} From 1c12992a05b67cd70ab44b2072cb8c30df2ffaf9 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 18:09:42 +0100 Subject: [PATCH 11/14] lxd/device/device/utils/network: network.VLANInterfaceCreate usage Signed-off-by: Thomas Parrott --- lxd/device/device_utils_network.go | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/lxd/device/device_utils_network.go b/lxd/device/device_utils_network.go index eb95d3501b..6db9656885 100644 --- a/lxd/device/device_utils_network.go +++ b/lxd/device/device_utils_network.go @@ -18,7 +18,6 @@ import ( "github.com/lxc/lxd/lxd/project" "github.com/lxc/lxd/lxd/revert" "github.com/lxc/lxd/lxd/state" - "github.com/lxc/lxd/lxd/util" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/logger" "github.com/lxc/lxd/shared/units" @@ -111,23 +110,12 @@ func networkRemoveInterfaceIfNeeded(state *state.State, nic string, current inst // networkCreateVlanDeviceIfNeeded creates a VLAN device if doesn't already exist. func networkCreateVlanDeviceIfNeeded(state *state.State, parent string, vlanDevice string, vlanID string) (string, error) { if vlanID != "" { - if !shared.PathExists(fmt.Sprintf("/sys/class/net/%s", vlanDevice)) { - // Bring the parent interface up so we can add a vlan to it. - _, err := shared.RunCommand("ip", "link", "set", "dev", parent, "up") - if err != nil { - return "", fmt.Errorf("Failed to bring up parent %s: %v", parent, err) - } - - // Add VLAN interface on top of parent. - _, err = shared.RunCommand("ip", "link", "add", "link", parent, "name", vlanDevice, "up", "type", "vlan", "id", vlanID) - if err != nil { - return "", err - } - - // Attempt to disable IPv6 router advertisement acceptance. - util.SysctlSet(fmt.Sprintf("net/ipv6/conf/%s/accept_ra", vlanDevice), "0") + created, err := network.VLANInterfaceCreate(parent, vlanDevice, vlanID) + if err != nil { + return "", err + } - // We created a new vlan interface, return true. + if created { return "created", nil } From f229e6614a3d544a5798c58a1957efa2199c3669 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 17:31:24 +0100 Subject: [PATCH 12/14] doc/networks: Clarifies use of ovn ranges settings in bridge network Signed-off-by: Thomas Parrott --- doc/networks.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/networks.md b/doc/networks.md index 47d3386963..a9d3cb6c25 100644 --- a/doc/networks.md +++ b/doc/networks.md @@ -83,7 +83,7 @@ ipv4.firewall | boolean | ipv4 address | true ipv4.nat.address | string | ipv4 address | - | The source address used for outbound traffic from the bridge ipv4.nat | boolean | ipv4 address | false | Whether to NAT (will default to true if unset and a random ipv4.address is generated) ipv4.nat.order | string | ipv4 address | before | Whether to add the required NAT rules before or after any pre-existing rules -ipv4.ovn.ranges | string | - | none | Comma separate list of IPv4 ranges to use for child OVN networks (FIRST-LAST format) +ipv4.ovn.ranges | string | - | none | Comma separate list of IPv4 ranges to use for child OVN network routers (FIRST-LAST format) ipv4.routes | string | ipv4 address | - | Comma separated list of additional IPv4 CIDR subnets to route to the bridge ipv4.routing | boolean | ipv4 address | true | Whether to route traffic in and out of the bridge ipv6.address | string | standard mode | random unused subnet | IPv6 address for the bridge (CIDR notation). Use "none" to turn off IPv6 or "auto" to generate a new one @@ -95,7 +95,7 @@ ipv6.firewall | boolean | ipv6 address | true ipv6.nat.address | string | ipv6 address | - | The source address used for outbound traffic from the bridge ipv6.nat | boolean | ipv6 address | false | Whether to NAT (will default to true if unset and a random ipv6.address is generated) ipv6.nat.order | string | ipv6 address | before | Whether to add the required NAT rules before or after any pre-existing rules -ipv6.ovn.ranges | string | - | none | Comma separate list of IPv6 ranges to use for child OVN networks (FIRST-LAST format) +ipv6.ovn.ranges | string | - | none | Comma separate list of IPv6 ranges to use for child OVN network routers (FIRST-LAST format) ipv6.routes | string | ipv6 address | - | Comma separated list of additional IPv6 CIDR subnets to route to the bridge ipv6.routing | boolean | ipv6 address | true | Whether to route traffic in and out of the bridge maas.subnet.ipv4 | string | ipv4 address | - | MAAS IPv4 subnet to register instances in (when using `network` property on nic) From 08abb33dfaaa4971d59d6e435cc69ec722c05699 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 17:31:45 +0100 Subject: [PATCH 13/14] doc/networks: Adds docs for physical network type Signed-off-by: Thomas Parrott --- doc/networks.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/networks.md b/doc/networks.md index a9d3cb6c25..6df04b13c4 100644 --- a/doc/networks.md +++ b/doc/networks.md @@ -6,6 +6,7 @@ LXD supports the following network types: - [macvlan](#network-macvlan): Provides preset configuration to use when connecting instances to a parent macvlan interface. - [sriov](#network-sriov): Provides preset configuration to use when connecting instances to a parent SR-IOV interface. - [ovn](#network-ovn): Creates a logical network using the OVN software defined networking system. + - [physical](#network-physical): Provides preset configuration to use when connecting OVN networks to a parent interface. The desired type can be specified using the `--type` argument, e.g. @@ -299,3 +300,22 @@ ipv4.address | string | standard mode | random unu ipv6.address | string | standard mode | random unused subnet | IPv6 address for the bridge (CIDR notation). Use "none" to turn off IPv6 or "auto" to generate a new one ipv6.dhcp.stateful | boolean | ipv6 dhcp | false | Whether to allocate addresses using DHCP network | string | - | - | Parent network to use for outbound external network access + +## network: physical + +The physical network type allows one to specify presets to use when connecting OVN networks to a parent interface. + +Network configuration properties: + +Key | Type | Condition | Default | Description +:-- | :-- | :-- | :-- | :-- +maas.subnet.ipv4 | string | ipv4 address | - | MAAS IPv4 subnet to register instances in (when using `network` property on nic) +maas.subnet.ipv6 | string | ipv6 address | - | MAAS IPv6 subnet to register instances in (when using `network` property on nic) +mtu | integer | - | - | The MTU of the new interface +parent | string | - | - | Parent interface to create sriov NICs on +vlan | integer | - | - | The VLAN ID to attach to +ipv4.gateway | string | standard mode | - | IPv4 address for the gateway and network (CIDR notation) +ipv4.ovn.ranges | string | - | none | Comma separate list of IPv4 ranges to use for child OVN network routers (FIRST-LAST format) +ipv6.gateway | string | standard mode | - | IPv6 address for the gateway and network (CIDR notation) +ipv6.ovn.ranges | string | - | none | Comma separate list of IPv6 ranges to use for child OVN network routers (FIRST-LAST format) +dns.nameservers | string | standard mode | - | List of DNS server IPs on physical network From 6a377d286621cd0682ee5d9fcc3b2e3556bab5bf Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 6 Oct 2020 16:50:15 +0100 Subject: [PATCH 14/14] lxd/network/driver/ovn: Adds support for physical network as uplink Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 115 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 445e555433..c748c0d734 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -363,6 +363,8 @@ func (n *ovn) setupParentPort(routerMAC net.HardwareAddr) (*ovnParentVars, error switch parentNet.Type() { case "bridge": return n.setupParentPortBridge(parentNet, routerMAC) + case "physical": + return n.setupParentPortPhysical(parentNet, routerMAC) } return nil, fmt.Errorf("Failed setting up parent port, network type %q unsupported as OVN parent", parentNet.Type()) @@ -389,6 +391,17 @@ func (n *ovn) setupParentPortBridge(parentNet Network, routerMAC net.HardwareAdd return v, nil } +// setupParentPortPhysical allocates external IPs on the parent network. +// Returns the derived ovnParentVars settings. +func (n *ovn) setupParentPortPhysical(parentNet Network, routerMAC net.HardwareAddr) (*ovnParentVars, error) { + v, err := n.allocateParentPortIPs(parentNet, routerMAC) + if err != nil { + return nil, errors.Wrapf(err, "Failed allocating parent port IPs on network %q", parentNet.Name()) + } + + return v, nil +} + // allocateParentPortIPs attempts to find a free IP in the parent network's OVN ranges and then stores it in // ovnVolatileParentIPv4 and ovnVolatileParentIPv6 config keys on this network. Returns ovnParentVars settings. func (n *ovn) allocateParentPortIPs(parentNet Network, routerMAC net.HardwareAddr) (*ovnParentVars, error) { @@ -632,6 +645,8 @@ func (n *ovn) startParentPort() error { switch parentNet.Type() { case "bridge": return n.startParentPortBridge(parentNet) + case "physical": + return n.startParentPortPhysical(parentNet) } return fmt.Errorf("Failed starting parent port, network type %q unsupported as OVN parent", parentNet.Type()) @@ -745,6 +760,59 @@ func (n *ovn) startParentPortBridge(parentNet Network) error { return nil } +// startParentPortPhysical creates OVS bridge (if doesn't exist) and connects parent interface to the OVS bridge. +func (n *ovn) startParentPortPhysical(parentNet Network) error { + vars := n.parentPortBridgeVars(parentNet) + + // Do this after gaining lock so that on failure we revert before release locking. + revert := revert.New() + defer revert.Fail() + + parentConfig := parentNet.Config() + uplinkDev := GetHostDevice(parentConfig["parent"], parentConfig["vlan"]) + + _, err := VLANInterfaceCreate(parentConfig["parent"], uplinkDev, parentConfig["vlan"]) + if err != nil { + return err + } + + // Ensure correct sysctls are set on uplink interface to avoid getting IPv6 link-local addresses. + _, err = shared.RunCommand("sysctl", + fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6=1", uplinkDev), + fmt.Sprintf("net/ipv6/conf/%s/forwarding=0", uplinkDev), + ) + if err != nil { + return errors.Wrapf(err, "Failed to configure uplink interface %q", uplinkDev) + } + + // Create parent OVS bridge if needed. + ovs := openvswitch.NewOVS() + err = ovs.BridgeAdd(vars.ovsBridge, true) + if err != nil { + return errors.Wrapf(err, "Failed to create parent uplink OVS bridge %q", vars.ovsBridge) + } + + // Connect OVS end veth interface to OVS bridge. + err = ovs.BridgePortAdd(vars.ovsBridge, uplinkDev, true) + if err != nil { + return errors.Wrapf(err, "Failed to connect uplink VF interface %q to parent OVS bridge %q", uplinkDev, vars.ovsBridge) + } + + // Associate OVS bridge to logical OVN provider. + err = ovs.OVNBridgeMappingAdd(vars.ovsBridge, parentNet.Name()) + if err != nil { + return errors.Wrapf(err, "Failed to associate parent OVS bridge %q to OVN provider %q", vars.ovsBridge, parentNet.Name()) + } + + _, err = shared.RunCommand("ip", "link", "set", uplinkDev, "up") + if err != nil { + return errors.Wrapf(err, "Failed to bring up parent interface %q", uplinkDev) + } + + revert.Success() + return nil +} + // deleteParentPort deletes the parent uplink connection. func (n *ovn) deleteParentPort() error { // Parent network must be in default project. @@ -761,6 +829,8 @@ func (n *ovn) deleteParentPort() error { switch parentNet.Type() { case "bridge": return n.deleteParentPortBridge(parentNet) + case "physical": + return n.deleteParentPortPhysical(parentNet) } return fmt.Errorf("Failed deleting parent port, network type %q unsupported as OVN parent", parentNet.Type()) @@ -819,6 +889,51 @@ func (n *ovn) deleteParentPortBridge(parentNet Network) error { return nil } +// deleteParentPortPhysical deletes parent uplink OVS bridge, OVN bridge mappings if not in use. +func (n *ovn) deleteParentPortPhysical(parentNet Network) error { + // Check OVS uplink bridge exists, if it does, check how many ports it has. + releaseIF := false + vars := n.parentPortBridgeVars(parentNet) + if shared.PathExists(fmt.Sprintf("/sys/class/net/%s", vars.ovsBridge)) { + ovs := openvswitch.NewOVS() + ports, err := ovs.BridgePortList(vars.ovsBridge) + if err != nil { + return err + } + + // If the OVS bridge has only 1 port (the parent interface) or fewer connected then delete it. + if len(ports) <= 1 { + releaseIF = true + + err = ovs.OVNBridgeMappingDelete(vars.ovsBridge, parentNet.Name()) + if err != nil { + return err + } + + err = ovs.BridgeDelete(vars.ovsBridge) + if err != nil { + return err + } + } + } else { + releaseIF = true // Remove the veths if OVS bridge already gone. + } + + // Remove the veth interfaces if they exist. + if releaseIF { + parentConfig := parentNet.Config() + parentDev := GetHostDevice(parentConfig["parent"], parentConfig["vlan"]) + if parentDev != "" && shared.PathExists(fmt.Sprintf("/sys/class/net/%s", parentDev)) { + _, err := shared.RunCommand("ip", "link", "set", parentDev, "down") + if err != nil { + return errors.Wrapf(err, "Failed to bring down parent interface %q", parentDev) + } + } + } + + return nil +} + // FillConfig fills requested config with any default values. func (n *ovn) FillConfig(config map[string]string) error { if config["ipv4.address"] == "" { From lxc-bot at linuxcontainers.org Tue Oct 6 19:29:23 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Tue, 06 Oct 2020 12:29:23 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/apparmor/forkproxy: Socket path fixes Message-ID: <5f7cc593.1c69fb81.68f2.b418SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 533 bytes Desc: not available URL: -------------- next part -------------- From f69844954acb125f4adeb3cfc04fc03f8696eede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 6 Oct 2020 15:27:57 -0400 Subject: [PATCH] lxd/apparmor/forkproxy: Socket path fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This handles two issues: - Symlinks that need to be dereferenced. - Ubuntu Core systems where the initial path should be used rather than the HostPath. Closes #5009 Signed-off-by: Stéphane Graber --- lxd/apparmor/instance_forkproxy.go | 31 ++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/lxd/apparmor/instance_forkproxy.go b/lxd/apparmor/instance_forkproxy.go index 05fe662460..7b320f0056 100644 --- a/lxd/apparmor/instance_forkproxy.go +++ b/lxd/apparmor/instance_forkproxy.go @@ -99,7 +99,13 @@ func forkproxyProfile(state *state.State, inst instance, dev device) (string, er fields := strings.SplitN(dev.Config()["listen"], ":", 2) if fields[0] == "unix" && !strings.HasPrefix(fields[1], "@") { if dev.Config()["bind"] == "host" || dev.Config()["bind"] == "" { - sockets = append(sockets, shared.HostPath(fields[1])) + hostPath := shared.HostPath(fields[1]) + sockets = append(sockets, hostPath) + + if hostPath != fields[1] { + // AppArmor can get confused on Ubuntu Core so allow both paths. + sockets = append(sockets, fields[1]) + } } else { sockets = append(sockets, fields[1]) } @@ -110,8 +116,29 @@ func forkproxyProfile(state *state.State, inst instance, dev device) (string, er if dev.Config()["bind"] == "host" || dev.Config()["bind"] == "" { sockets = append(sockets, fields[1]) } else { - sockets = append(sockets, shared.HostPath(fields[1])) + hostPath := shared.HostPath(fields[1]) + sockets = append(sockets, hostPath) + + if hostPath != fields[1] { + // AppArmor can get confused on Ubuntu Core so allow both paths. + sockets = append(sockets, fields[1]) + } + } + } + + // AppArmor requires deref of all paths. + for k := range sockets { + // Skip non-existing because of the additional entry for the host side. + if !shared.PathExists(sockets[k]) { + continue } + + v, err := filepath.EvalSymlinks(sockets[k]) + if err != nil { + return "", err + } + + sockets[k] = v } // Render the profile. From lxc-bot at linuxcontainers.org Tue Oct 6 19:47:00 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Tue, 06 Oct 2020 12:47:00 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/images: Fix crash when no "info" struct Message-ID: <5f7cc9b4.1c69fb81.c01a4.b673SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 648 bytes Desc: not available URL: -------------- next part -------------- From fa9f3d3f4352d9c570e0ae97e41e2319297286d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 6 Oct 2020 15:40:43 -0400 Subject: [PATCH] lxd/images: Fix crash when no "info" struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a crash happening when all of those conditions are met: - Clustered environment - Image is downloaded using the LXD protocol - Source image is marked as private - Image was downloaded previously to the cluster - The server processing the download doesn't have the image Signed-off-by: Stéphane Graber --- lxd/daemon_images.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/daemon_images.go b/lxd/daemon_images.go index d17e523d28..7e272a3a89 100644 --- a/lxd/daemon_images.go +++ b/lxd/daemon_images.go @@ -119,13 +119,13 @@ func (d *Daemon) ImageDownload(op *operations.Operation, server string, protocol if nodeAddress != "" { // The image is available from another node, let's try to import it. - err = instanceImageTransfer(d, project, info.Fingerprint, nodeAddress) + err = instanceImageTransfer(d, project, imgInfo.Fingerprint, nodeAddress) if err != nil { return nil, errors.Wrapf(err, "Failed transferring image") } // As the image record already exists in the project, just add the node ID to the image. - err = d.cluster.AddImageToLocalNode(project, info.Fingerprint) + err = d.cluster.AddImageToLocalNode(project, imgInfo.Fingerprint) if err != nil { return nil, errors.Wrapf(err, "Failed adding image to local node") } From lxc-bot at linuxcontainers.org Wed Oct 7 14:24:50 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Wed, 07 Oct 2020 07:24:50 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: Handle uplink network changes in OVN networks Message-ID: <5f7dcfb2.1c69fb81.893a0.5cfcSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- Network: Handle uplink network changes in OVN networks by tomponline · Pull Request #7993 · lxc/lxd · GitHub
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Network: Handle uplink network changes in OVN networks #7993

Draft
wants to merge 4 commits into
base: master
from

Conversation

@tomponline
Copy link
Member

@tomponline tomponline commented Oct 7, 2020

No description provided.

tomponline added 4 commits Oct 7, 2020
…f in use

Also corrects typo in comment.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
…t LXD DB

Rather than using the presence of a patch port connected to the local uplink OVS bridge, as this is not always reliable (it can take time to be released).

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
 - Stop old parent port on old uplink network.
 - Remove old volatile IP keys.
 - Start new parent port on changed uplink network.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked issues

Successfully merging this pull request may close these issues.

None yet

1 participant
You can’t perform that action at this time.
From lxc-bot at linuxcontainers.org Wed Oct 7 15:49:08 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Wed, 07 Oct 2020 08:49:08 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Storage: Gives clear error message when trying to create duplicate storage pool in single node Message-ID: <5f7de374.1c69fb81.6551b.4891SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 497 bytes Desc: not available URL: -------------- next part -------------- From 2983af787e86c2e8d42d9f55ae0b9d5ae198f55f Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 7 Oct 2020 16:47:24 +0100 Subject: [PATCH] lxd/storage/pools: Gives clear error message when trying to create duplicate storage pool in single node Previously it errored with: `Error: Pool not defined on nodes: none` As was falling through into cluster storage pool setup mode. Signed-off-by: Thomas Parrott --- lxd/storage_pools.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lxd/storage_pools.go b/lxd/storage_pools.go index b24443404b..01ce07e034 100644 --- a/lxd/storage_pools.go +++ b/lxd/storage_pools.go @@ -159,6 +159,8 @@ func storagePoolsPost(d *Daemon, r *http.Request) response.Response { } return resp } + + return response.BadRequest(fmt.Errorf("The storage pool already exists")) } // No targetNode was specified and we're clustered, so finalize the From lxc-bot at linuxcontainers.org Wed Oct 7 19:42:13 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Wed, 07 Oct 2020 12:42:13 -0700 (PDT) Subject: [lxc-devel] [pylxd/master] client: Add basic support for projects Message-ID: <5f7e1a15.1c69fb81.45315.bbe3SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 463 bytes Desc: not available URL: -------------- next part -------------- client: Add basic support for projects by stgraber · Pull Request #414 · lxc/pylxd · GitHub
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

client: Add basic support for projects #414

Open
wants to merge 1 commit into
base: master
from
Open

Conversation

@stgraber
Copy link
Member

@stgraber stgraber commented Oct 7, 2020

This allows passing the project name as a string to Client and have it
automatically added to all URLs.

Signed-off-by: Stéphane Graber stgraber at ubuntu.com

This allows passing the project name as a string to Client and have it
automatically added to all URLs.

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked issues

Successfully merging this pull request may close these issues.

None yet

1 participant
You can’t perform that action at this time.
From lxc-bot at linuxcontainers.org Thu Oct 8 02:18:05 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Wed, 07 Oct 2020 19:18:05 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Restrict events API Message-ID: <5f7e76dd.1c69fb81.b8142.3bacSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 811a47db881650b7b7d37f6a4a42261ee2f28fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 7 Oct 2020 22:10:22 -0400 Subject: [PATCH 1/2] lxd/events: Validate type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/events.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lxd/events.go b/lxd/events.go index afdbe93633..4cf186e40f 100644 --- a/lxd/events.go +++ b/lxd/events.go @@ -2,6 +2,7 @@ package main import ( "context" + "fmt" "net/http" "strings" @@ -11,6 +12,8 @@ import ( "github.com/lxc/lxd/shared/logger" ) +var eventTypes = []string{"logging", "operation", "lifecycle"} + var eventsCmd = APIEndpoint{ Path: "events", @@ -32,9 +35,17 @@ func (r *eventsServe) String() string { func eventsSocket(d *Daemon, r *http.Request, w http.ResponseWriter) error { project := projectParam(r) - typeStr := r.FormValue("type") - if typeStr == "" { - typeStr = "logging,operation,lifecycle" + types := strings.Split(r.FormValue("type"), ",") + if len(types) == 1 && types[0] == "" { + types = eventTypes + } + + // Validate event types. + for _, entry := range types { + if !shared.StringInSlice(entry, eventTypes) { + response.BadRequest(fmt.Errorf("'%s' isn't a supported event type", entry)).Render(w) + return nil + } } // Upgrade the connection to websocket @@ -59,7 +70,7 @@ func eventsSocket(d *Daemon, r *http.Request, w http.ResponseWriter) error { // If this request is an internal one initiated by another node wanting // to watch the events on this node, set the listener to broadcast only // local events. - listener, err := d.events.AddListener(project, c, strings.Split(typeStr, ","), serverName, isClusterNotification(r)) + listener, err := d.events.AddListener(project, c, types, serverName, isClusterNotification(r)) if err != nil { return err } From bdef9858b684a6e4a976fd1491668d209d310cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 7 Oct 2020 22:17:32 -0400 Subject: [PATCH 2/2] lxd/events: Prevent logging access to non-admin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/events.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lxd/events.go b/lxd/events.go index 4cf186e40f..1c58ce99db 100644 --- a/lxd/events.go +++ b/lxd/events.go @@ -48,6 +48,11 @@ func eventsSocket(d *Daemon, r *http.Request, w http.ResponseWriter) error { } } + if shared.StringInSlice("logging", types) && !d.userIsAdmin(r) { + response.Forbidden(nil).Render(w) + return nil + } + // Upgrade the connection to websocket c, err := shared.WebsocketUpgrader.Upgrade(w, r, nil) if err != nil { From lxc-bot at linuxcontainers.org Thu Oct 8 02:35:59 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Wed, 07 Oct 2020 19:35:59 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Improve graceful shutdown Message-ID: <5f7e7b0f.1c69fb81.5eb31.b181SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 218d76f7b5d515019717a4a645984d59ad1baaa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 7 Oct 2020 22:26:14 -0400 Subject: [PATCH 1/2] lxd/daemon: Clean shutdown on SIGPWR/SIGTERM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/main_daemon.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/lxd/main_daemon.go b/lxd/main_daemon.go index 9cf3f7aad5..99ad98ea53 100644 --- a/lxd/main_daemon.go +++ b/lxd/main_daemon.go @@ -77,13 +77,30 @@ func (c *cmdDaemon) Run(cmd *cobra.Command, args []string) error { signal.Notify(chIgnore, unix.SIGHUP) s := d.State() + + cleanStop := func() { + // Cancelling the context will make everyone aware that we're shutting down. + d.cancel() + + // waitForOperations will block until all operations are done, or it's forced to shut down. + // For the latter case, we re-use the shutdown channel which is filled when a shutdown is + // initiated using `lxd shutdown`. + waitForOperations(s, d.shutdownChan) + + d.Kill() + } + select { case sig := <-ch: if sig == unix.SIGPWR { - logger.Infof("Received '%s signal', shutting down instances", sig) - d.Kill() + logger.Infof("Received '%s signal', waiting for all operations to finish", sig) + cleanStop() + instancesShutdown(s) networkShutdown(s) + } else if sig == unix.SIGTERM { + logger.Infof("Received '%s signal', waiting for all operations to finish", sig) + cleanStop() } else { logger.Infof("Received '%s signal', exiting", sig) d.Kill() @@ -91,14 +108,8 @@ func (c *cmdDaemon) Run(cmd *cobra.Command, args []string) error { case <-d.shutdownChan: logger.Infof("Asked to shutdown by API, waiting for all operations to finish") - // Cancelling the context will make everyone aware that we're shutting down. - d.cancel() - // waitForOperations will block until all operations are done, or it's forced to shut down. - // For the latter case, we re-use the shutdown channel which is filled when a shutdown is - // initiated using `lxd shutdown`. - waitForOperations(s, d.shutdownChan) + cleanStop() - d.Kill() instancesShutdown(s) networkShutdown(s) } From 33b7ecc2c230462ce42014bcb7a82c6e0887c09d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 7 Oct 2020 22:34:47 -0400 Subject: [PATCH 2/2] lxd/operations: Don't directly trigger shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/operations.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lxd/operations.go b/lxd/operations.go index e1815a02ef..aa982b7034 100644 --- a/lxd/operations.go +++ b/lxd/operations.go @@ -95,8 +95,7 @@ func waitForOperations(s *state.State, chCancel chan struct{}) { // If there are still running operations, we shut down the instances // which will terminate the operations. if execConsoleOps > 0 { - logger.Info("Shutdown timeout reached, shutting down instances") - instancesShutdown(s) + logger.Info("Timeout reached, continuing with shutdown") } case <-logTick: From lxc-bot at linuxcontainers.org Thu Oct 8 07:05:43 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Thu, 08 Oct 2020 00:05:43 -0700 (PDT) Subject: [lxc-devel] [lxc-ci/master] jenkins/jobs: Update Apertis releases Message-ID: <5f7eba47.1c69fb81.76ac4.f21bSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 357 bytes Desc: not available URL: -------------- next part -------------- From 054ff0d6217bd82df3ea4c1353c2f075a490635d Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Tue, 6 Oct 2020 12:01:30 +0200 Subject: [PATCH] jenkins/jobs: Update Apertis releases Signed-off-by: Thomas Hipp --- jenkins/jobs/image-apertis.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jenkins/jobs/image-apertis.yaml b/jenkins/jobs/image-apertis.yaml index 7a4748a..1202851 100644 --- a/jenkins/jobs/image-apertis.yaml +++ b/jenkins/jobs/image-apertis.yaml @@ -18,8 +18,8 @@ name: release type: user-defined values: - - 'v2019.3' - - 'v2020.1' + - 'v2019.4' + - 'v2020.2' - axis: name: variant From lxc-bot at linuxcontainers.org Thu Oct 8 08:16:47 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Thu, 08 Oct 2020 01:16:47 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/instance/drivers: Add USB controller to VMs Message-ID: <5f7ecaef.1c69fb81.c872b.8f55SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 87e44ddcf8dfaa843b7375601aba550182217020 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Thu, 8 Oct 2020 09:22:56 +0200 Subject: [PATCH 1/2] lxd/instance/drivers: Enable USB for VMs This enables USB for VMs. Signed-off-by: Thomas Hipp --- lxd/instance/drivers/driver_qemu_templates.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/instance/drivers/driver_qemu_templates.go b/lxd/instance/drivers/driver_qemu_templates.go index 6950919840..2e825176b0 100644 --- a/lxd/instance/drivers/driver_qemu_templates.go +++ b/lxd/instance/drivers/driver_qemu_templates.go @@ -23,7 +23,7 @@ type = "pseries" type = "s390-ccw-virtio" {{end -}} accel = "kvm" -usb = "off" +usb = "on" graphics = "off" {{if eq .architecture "x86_64" -}} From a6c11f1114c67cb1c18a2c9ba83a5feeb92e5820 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Thu, 8 Oct 2020 10:06:49 +0200 Subject: [PATCH 2/2] lxd/instance/drivers: Add USB controller to QEMU config This adds a USB controller to the QEMU config. Signed-off-by: Thomas Hipp --- lxd/instance/drivers/driver_qemu.go | 11 +++++++++++ lxd/instance/drivers/driver_qemu_templates.go | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 62c1de7117..0b697cbf58 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -1749,6 +1749,17 @@ func (vm *qemu) generateQemuConfigFile(busName string, devConfs []*deviceConfig. return "", err } + devBus, devAddr, multi = bus.allocate(busFunctionGroupGeneric) + err = qemuUSB.Execute(sb, map[string]interface{}{ + "bus": bus.name, + "devBus": devBus, + "devAddr": devAddr, + "multifunction": multi, + }) + if err != nil { + return "", err + } + devBus, devAddr, multi = bus.allocate(busFunctionGroupNone) err = qemuSCSI.Execute(sb, map[string]interface{}{ "bus": bus.name, diff --git a/lxd/instance/drivers/driver_qemu_templates.go b/lxd/instance/drivers/driver_qemu_templates.go index 2e825176b0..c1b383bc6b 100644 --- a/lxd/instance/drivers/driver_qemu_templates.go +++ b/lxd/instance/drivers/driver_qemu_templates.go @@ -497,3 +497,14 @@ x-vga = "on" multifunction = "on" {{- end }} `)) + +var qemuUSB = template.Must(template.New("qemuUSB").Parse(` +# USB controller +[device "qemu_usb"] +driver = "qemu-xhci" +bus = "{{.devBus}}" +addr = "{{.devAddr}}" +{{if .multifunction -}} +multifunction = "on" +{{- end }} +`)) From lxc-bot at linuxcontainers.org Thu Oct 8 13:12:32 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Thu, 08 Oct 2020 06:12:32 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/apparmor: Fix devPaths in QEMU profile Message-ID: <5f7f1040.1c69fb81.b40fa.7f5aSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 9165a2c1baacd875522cab545fbe70257a18b371 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Thu, 8 Oct 2020 15:11:57 +0200 Subject: [PATCH] lxd/apparmor: Fix devPaths in QEMU profile Use the devPaths instead of inst.DevPaths(), as the former evaluates symlinks. Signed-off-by: Thomas Hipp --- lxd/apparmor/instance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/apparmor/instance.go b/lxd/apparmor/instance.go index 77d266befd..6fd1a2a11e 100644 --- a/lxd/apparmor/instance.go +++ b/lxd/apparmor/instance.go @@ -176,7 +176,7 @@ func instanceProfile(state *state.State, inst instance) (string, error) { } err = qemuProfileTpl.Execute(sb, map[string]interface{}{ - "devPaths": inst.DevPaths(), + "devPaths": devPaths, "exePath": util.GetExecPath(), "libraryPath": strings.Split(os.Getenv("LD_LIBRARY_PATH"), ":"), "logPath": inst.LogPath(), From lxc-bot at linuxcontainers.org Thu Oct 8 14:54:56 2020 From: lxc-bot at linuxcontainers.org (freeekanayaka on Github) Date: Thu, 08 Oct 2020 07:54:56 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Retry harder upon transient database errors Message-ID: <5f7f2840.1c69fb81.6486e.e70eSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 347 bytes Desc: not available URL: -------------- next part -------------- From 6da737988a5fc96724446109a32c10e82bc4154b Mon Sep 17 00:00:00 2001 From: Free Ekanayaka Date: Thu, 8 Oct 2020 16:16:36 +0200 Subject: [PATCH 1/5] db: Retry transient errors for longer We used to retry for a littl more than a second, which is not enough in some cases. We now retry for about 25 seconds and with a higher attempt frequency. Signed-off-by: Free Ekanayaka --- lxd/db/query/retry.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lxd/db/query/retry.go b/lxd/db/query/retry.go index a67f3dc5ac..8c62026843 100644 --- a/lxd/db/query/retry.go +++ b/lxd/db/query/retry.go @@ -5,12 +5,16 @@ import ( "strings" "time" + "github.com/Rican7/retry/jitter" + "github.com/canonical/go-dqlite/driver" "github.com/mattn/go-sqlite3" "github.com/pkg/errors" "github.com/lxc/lxd/shared/logger" ) +const maxRetries = 250 + // Retry wraps a function that interacts with the database, and retries it in // case a transient error is hit. // @@ -18,7 +22,7 @@ import ( func Retry(f func() error) error { // TODO: the retry loop should be configurable. var err error - for i := 0; i < 5; i++ { + for i := 0; i < maxRetries; i++ { err = f() if err != nil { // No point in re-trying or logging a no-row error. @@ -29,8 +33,12 @@ func Retry(f func() error) error { // Process actual errors. logger.Debugf("Database error: %#v", err) if IsRetriableError(err) { + if i == maxRetries { + logger.Warnf("Give up retring database error: %v", err) + break + } logger.Debugf("Retry failed db interaction (%v)", err) - time.Sleep(250 * time.Millisecond) + time.Sleep(jitter.Deviation(nil, 0.8)(100 * time.Millisecond)) continue } } From 0978355a1ba22a6552ab8cb13bce99808dfcb014 Mon Sep 17 00:00:00 2001 From: Free Ekanayaka Date: Thu, 8 Oct 2020 16:18:10 +0200 Subject: [PATCH 2/5] db: Always retry driver.ErrBusy, regardless of the error message Signed-off-by: Free Ekanayaka --- lxd/db/query/retry.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lxd/db/query/retry.go b/lxd/db/query/retry.go index 8c62026843..b8a3fb1116 100644 --- a/lxd/db/query/retry.go +++ b/lxd/db/query/retry.go @@ -56,6 +56,10 @@ func IsRetriableError(err error) bool { return false } + if err, ok := err.(driver.Error); ok && err.Code == driver.ErrBusy { + return true + } + if err == sqlite3.ErrLocked || err == sqlite3.ErrBusy { return true } From 4ba063876dcec1ea8659ab74bd66dbec58fb4a33 Mon Sep 17 00:00:00 2001 From: Free Ekanayaka Date: Thu, 8 Oct 2020 16:23:23 +0200 Subject: [PATCH 3/5] db: Retry failed rollbacks if they are due to transient errors This avoids leaving the connection in a bad state, where a transaction is already in progress and another one can't be started. Signed-off-by: Free Ekanayaka --- lxd/db/query/transaction.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/db/query/transaction.go b/lxd/db/query/transaction.go index 0cde98614d..77bac41403 100644 --- a/lxd/db/query/transaction.go +++ b/lxd/db/query/transaction.go @@ -30,7 +30,7 @@ func Transaction(db *sql.DB, f func(*sql.Tx) error) error { // succeeds the given error is returned, otherwise a new error that wraps it // gets generated and returned. func rollback(tx *sql.Tx, reason error) error { - err := tx.Rollback() + err := Retry(tx.Rollback) if err != nil { logger.Warnf("Failed to rollback transaction after error (%v): %v", reason, err) } From f68785b88259f34af84c4360f06c33d69cbc4281 Mon Sep 17 00:00:00 2001 From: Free Ekanayaka Date: Thu, 8 Oct 2020 16:49:56 +0200 Subject: [PATCH 4/5] db: Explicitly rollback leftover transactions when a new one can't be started Signed-off-by: Free Ekanayaka --- lxd/db/query/transaction.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lxd/db/query/transaction.go b/lxd/db/query/transaction.go index 77bac41403..901177b0e8 100644 --- a/lxd/db/query/transaction.go +++ b/lxd/db/query/transaction.go @@ -2,6 +2,7 @@ package query import ( "database/sql" + "strings" "github.com/lxc/lxd/shared/logger" "github.com/pkg/errors" @@ -11,6 +12,11 @@ import ( func Transaction(db *sql.DB, f func(*sql.Tx) error) error { tx, err := db.Begin() if err != nil { + // If there is a leftover transaction let's try to rollback, + // we'll then retry again. + if strings.Contains(err.Error(), "cannot start a transaction within a transaction") { + db.Exec("ROLLBACK") + } return errors.Wrap(err, "failed to begin transaction") } From 54639c41eab9532e618be4b6d59a835c3ee6aee0 Mon Sep 17 00:00:00 2001 From: Free Ekanayaka Date: Thu, 8 Oct 2020 16:50:35 +0200 Subject: [PATCH 5/5] db: Retry to begin a new transaction after an explicit rollback attempt Signed-off-by: Free Ekanayaka --- lxd/db/query/retry.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lxd/db/query/retry.go b/lxd/db/query/retry.go index b8a3fb1116..0d8a28c032 100644 --- a/lxd/db/query/retry.go +++ b/lxd/db/query/retry.go @@ -68,6 +68,10 @@ func IsRetriableError(err error) bool { return true } + if strings.Contains(err.Error(), "cannot start a transaction within a transaction") { + return true + } + if strings.Contains(err.Error(), "bad connection") { return true } From lxc-bot at linuxcontainers.org Thu Oct 8 17:07:15 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Thu, 08 Oct 2020 10:07:15 -0700 (PDT) Subject: [lxc-devel] [lxd/master] More graceful shutdown fixes Message-ID: <5f7f4743.1c69fb81.bc307.d74aSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From f5d8caf7988e3d6e6fd0a772540d696c92e2bc7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 8 Oct 2020 11:18:34 -0400 Subject: [PATCH 1/2] lxd/operations: Fix timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/operations.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/operations.go b/lxd/operations.go index aa982b7034..e5d934c9ed 100644 --- a/lxd/operations.go +++ b/lxd/operations.go @@ -97,7 +97,7 @@ func waitForOperations(s *state.State, chCancel chan struct{}) { if execConsoleOps > 0 { logger.Info("Timeout reached, continuing with shutdown") } - + return case <-logTick: // Print log message every minute. logger.Infof("Waiting for %d operation(s) to finish", runningOps) From 3a6bf54b1ac3dc9156f267a7694aa4eb3f9b896c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 8 Oct 2020 13:06:49 -0400 Subject: [PATCH 2/2] lxd/daemon: Allow more operations during shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/daemon.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lxd/daemon.go b/lxd/daemon.go index a422ef8b20..e04cafc397 100644 --- a/lxd/daemon.go +++ b/lxd/daemon.go @@ -461,7 +461,25 @@ func (d *Daemon) createCmd(restAPI *mux.Router, version string, c APIEndpoint) { // - internal calls, e.g. lxd shutdown // - events endpoint as this is accessed when running `lxd shutdown` // - /1.0 endpoint - if version != "internal" && c.Path != "events" && c.Path != "" && d.ctx.Err() == context.Canceled { + // - /1.0/operations endpoints + // - GET queries + allowedDuringShutdown := func() bool { + if version == "internal" { + return true + } + + if c.Path == "" || c.Path == "events" || c.Path == "operations" || strings.HasPrefix(c.Path, "operations/") { + return true + } + + if r.Method == "GET" { + return true + } + + return false + } + + if d.ctx.Err() == context.Canceled && !allowedDuringShutdown() { response.Unavailable(fmt.Errorf("LXD is shutting down")).Render(w) return } From lxc-bot at linuxcontainers.org Thu Oct 8 19:25:04 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Thu, 08 Oct 2020 12:25:04 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/include: Relocate ifndef for NEWCGROUP Message-ID: <5f7f6790.1c69fb81.9988b.220bSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From 985e7cafd3e9afbe94b4781c736a84f957099c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 8 Oct 2020 15:23:37 -0400 Subject: [PATCH] lxd/include: Relocate ifndef for NEWCGROUP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/include/macro.h | 4 ++++ lxd/main_forksyscall.go | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lxd/include/macro.h b/lxd/include/macro.h index 6986a1f685..fc6dd72b55 100644 --- a/lxd/include/macro.h +++ b/lxd/include/macro.h @@ -278,4 +278,8 @@ enum { #define CLONE_NEWTIME 0x00000080 #endif +#ifndef CLONE_NEWCGROUP +#define CLONE_NEWCGROUP 0x02000000 +#endif + #endif /* __LXC_MACRO_H */ diff --git a/lxd/main_forksyscall.go b/lxd/main_forksyscall.go index b4a62a3b92..4b770e73e7 100644 --- a/lxd/main_forksyscall.go +++ b/lxd/main_forksyscall.go @@ -199,10 +199,6 @@ static void mknod_emulate(void) } } -#ifndef CLONE_NEWCGROUP -#define CLONE_NEWCGROUP 0x02000000 -#endif - const static int ns_flags[] = { CLONE_NEWUSER, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWCGROUP }; static bool change_creds(int pidfd, int ns_fd, cap_t caps, uid_t nsuid, gid_t nsgid, uid_t nsfsuid, gid_t nsfsgid) From lxc-bot at linuxcontainers.org Thu Oct 8 20:04:27 2020 From: lxc-bot at linuxcontainers.org (sparkiegeek on Github) Date: Thu, 08 Oct 2020 13:04:27 -0700 (PDT) Subject: [lxc-devel] [lxd/master] doc: Remove stray \_ escapes in security.md Message-ID: <5f7f70cb.1c69fb81.6059a.12dbSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 6c3a01f9e1a533dbc4283f02eb3b95ea17c5a8dd Mon Sep 17 00:00:00 2001 From: Adam Collard Date: Thu, 8 Oct 2020 21:02:29 +0100 Subject: [PATCH] doc: Remove stray \_ escapes in security.md Signed-off-by: Adam Collard --- doc/security.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/security.md b/doc/security.md index ad12291be4..a6721daf08 100644 --- a/doc/security.md +++ b/doc/security.md @@ -236,11 +236,11 @@ instance is using, or can be added to individual instances, as shown below. The following security features are available for `bridged` NICs: -Key | Type | Default | Required | Description -:-- | :-- | :-- | :-- | :-- -security.mac\_filtering | boolean | false | no | Prevent the instance from spoofing another's MAC address -security.ipv4\_filtering | boolean | false | no | Prevent the instance from spoofing another's IPv4 address (enables mac\_filtering) -security.ipv6\_filtering | boolean | false | no | Prevent the instance from spoofing another's IPv6 address (enables mac\_filtering) +Key | Type | Default | Required | Description +:-- | :-- | :-- | :-- | :-- +security.mac_filtering | boolean | false | no | Prevent the instance from spoofing another's MAC address +security.ipv4_filtering | boolean | false | no | Prevent the instance from spoofing another's IPv4 address (enables mac\_filtering) +security.ipv6_filtering | boolean | false | no | Prevent the instance from spoofing another's IPv6 address (enables mac\_filtering) One can override the default `bridged` NIC settings from the profile on a per-instance basis using: @@ -258,13 +258,13 @@ different MAC address (i.e using bridged or macvlan NICs). The IP filtering features block ARP and NDP advertisements that contain a spoofed IP, as well as blocking any packets that contain a spoofed source address. -If `security.ipv4\_filtering` or `security.ipv6\_filtering` is enabled and the instance cannot be allocated an IP +If `security.ipv4_filtering` or `security.ipv6_filtering` is enabled and the instance cannot be allocated an IP address (because `ipvX.address=none` or there is no DHCP service enabled on the bridge) then all IP traffic for that protocol is blocked from the instance. -When `security.ipv6\_filtering` is enabled IPv6 router advertisements are blocked from the instance. +When `security.ipv6_filtering` is enabled IPv6 router advertisements are blocked from the instance. -When `security.ipv4\_filtering` or `security.ipv6\_filtering` is enabled, any Ethernet frames that are not ARP, +When `security.ipv4_filtering` or `security.ipv6_filtering` is enabled, any Ethernet frames that are not ARP, IPv4 or IPv6 are dropped. This prevents stacked VLAN QinQ (802.1ad) frames from bypassing the IP filtering. ### Routed NIC security From lxc-bot at linuxcontainers.org Fri Oct 9 11:29:56 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Fri, 09 Oct 2020 04:29:56 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxc-to-lxd: Handle snap better Message-ID: <5f8049b4.1c69fb81.5a5e6.b12eSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 445 bytes Desc: not available URL: -------------- next part -------------- From 11450c0a9c9c428a7f27f4cc90da05e78b5828a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 9 Oct 2020 07:29:24 -0400 Subject: [PATCH] lxc-to-lxd: Handle snap better MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxc-to-lxd/main_migrate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxc-to-lxd/main_migrate.go b/lxc-to-lxd/main_migrate.go index 5012afbe85..7937b847f0 100644 --- a/lxc-to-lxd/main_migrate.go +++ b/lxc-to-lxd/main_migrate.go @@ -71,7 +71,7 @@ func (c *cmdMigrate) RunE(cmd *cobra.Command, args []string) error { } // Retrieve LXC containers - for _, container := range liblxc.Containers(c.flagLXCPath) { + for _, container := range liblxc.Containers(shared.HostPathFollow(c.flagLXCPath)) { if !c.flagAll && !shared.StringInSlice(container.Name(), c.flagContainers) { continue } From lxc-bot at linuxcontainers.org Fri Oct 9 14:43:07 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Fri, 09 Oct 2020 07:43:07 -0700 (PDT) Subject: [lxc-devel] [lxd/master] USB passthrough for VMs Message-ID: <5f8076fb.1c69fb81.39ae6.5d7eSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From e1e86a1d38e28c6c8d95aae6b5feb6cd2bf74dae Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Thu, 8 Oct 2020 10:57:55 +0200 Subject: [PATCH 1/7] lxd/device/usb: Allow USB devices for VMs This removes the container-only restriction, and allows USB devices for VMs. Signed-off-by: Thomas Hipp --- lxd/device/usb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/device/usb.go b/lxd/device/usb.go index 6e5c09aab9..519da70edb 100644 --- a/lxd/device/usb.go +++ b/lxd/device/usb.go @@ -45,7 +45,7 @@ func (d *usb) isRequired() bool { // validateConfig checks the supplied config for correctness. func (d *usb) validateConfig(instConf instance.ConfigReader) error { - if !instanceSupported(instConf.Type(), instancetype.Container) { + if !instanceSupported(instConf.Type(), instancetype.Container, instancetype.VM) { return ErrUnsupportedDevType } From eadab2d3d214010edeaf8c4c04df59caf94858fb Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Thu, 8 Oct 2020 16:30:22 +0200 Subject: [PATCH 2/7] lxd/device: Add bus and dev number to USBEvent This adds two new public fields BusNum and DevNum to the USBEvent struct. Signed-off-by: Thomas Hipp --- lxd/device/device_utils_usb_events.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lxd/device/device_utils_usb_events.go b/lxd/device/device_utils_usb_events.go index a0a2f01e11..18840eafb2 100644 --- a/lxd/device/device_utils_usb_events.go +++ b/lxd/device/device_utils_usb_events.go @@ -26,6 +26,9 @@ type USBEvent struct { Minor uint32 UeventParts []string UeventLen int + + BusNum int + DevNum int } // usbHandlers stores the event handler callbacks for USB events. @@ -106,14 +109,16 @@ func USBNewEvent(action string, vendor string, product string, major string, min return USBEvent{}, err } + busnumInt := 0 + devnumInt := 0 path := devname if devname == "" { - busnumInt, err := strconv.Atoi(busnum) + busnumInt, err = strconv.Atoi(busnum) if err != nil { return USBEvent{}, err } - devnumInt, err := strconv.Atoi(devnum) + devnumInt, err = strconv.Atoi(devnum) if err != nil { return USBEvent{}, err } @@ -133,5 +138,7 @@ func USBNewEvent(action string, vendor string, product string, major string, min uint32(minorInt), ueventParts, ueventLen, + busnumInt, + devnumInt, }, nil } From bf3c02d11b68ee13da5eca32e61a926adf216eff Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 9 Oct 2020 16:36:25 +0200 Subject: [PATCH 3/7] lxd/apparmor: Allow USB specific paths This adds some paths to the AppArmor profile which need to be accessible when using USB pass through. Signed-off-by: Thomas Hipp --- lxd/apparmor/instance_qemu.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lxd/apparmor/instance_qemu.go b/lxd/apparmor/instance_qemu.go index c529e76ffc..a1183e27e4 100644 --- a/lxd/apparmor/instance_qemu.go +++ b/lxd/apparmor/instance_qemu.go @@ -27,9 +27,13 @@ profile "{{ .name }}" flags=(attach_disconnected,mediate_deleted) { /dev/vhost-net rw, /dev/vhost-vsock rw, /etc/ceph/** r, + /run/udev/data/* r, + /sys/bus/ r, /sys/bus/nd/devices/ r, - /sys/devices/system/node/ r, - /sys/devices/system/node/** r, + /sys/bus/usb/devices/ r, + /sys/bus/usb/devices/** r, + /sys/class/ r, + /sys/devices/** r, /sys/module/vhost/** r, /{,usr/}bin/qemu* mrix, /usr/share/OVMF/OVMF_CODE.fd kr, From 1a811e7e7ca3f82d3b507d7b5462c1ccd3de579a Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 9 Oct 2020 16:37:07 +0200 Subject: [PATCH 4/7] lxd/device/config: Add USBDevice to RunConfig Signed-off-by: Thomas Hipp --- lxd/device/config/device_runconfig.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lxd/device/config/device_runconfig.go b/lxd/device/config/device_runconfig.go index eba388047a..f68d77b27e 100644 --- a/lxd/device/config/device_runconfig.go +++ b/lxd/device/config/device_runconfig.go @@ -42,4 +42,5 @@ type RunConfig struct { Uevents [][]string // Uevents to inject. PostHooks []func() error // Functions to be run after device attach/detach. GPUDevice []RunConfigItem // GPU device configuration settings. + USBDevice []RunConfigItem // USB device configuration settings. } From e1cd9f34e268837d932c8842dfbe39cfe85962ac Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 9 Oct 2020 16:38:01 +0200 Subject: [PATCH 5/7] lxd/device: Handle USB devices for VMs Signed-off-by: Thomas Hipp --- lxd/device/usb.go | 57 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/lxd/device/usb.go b/lxd/device/usb.go index 519da70edb..be646839fd 100644 --- a/lxd/device/usb.go +++ b/lxd/device/usb.go @@ -118,6 +118,14 @@ func (d *usb) Register() error { // Start is run when the device is added to the instance. func (d *usb) Start() (*deviceConfig.RunConfig, error) { + if d.inst.Type() == instancetype.VM { + return d.startVM() + } + + return d.startContainer() +} + +func (d *usb) startContainer() (*deviceConfig.RunConfig, error) { usbs, err := d.loadUsb() if err != nil { return nil, err @@ -144,18 +152,48 @@ func (d *usb) Start() (*deviceConfig.RunConfig, error) { return &runConf, nil } +func (d *usb) startVM() (*deviceConfig.RunConfig, error) { + usbs, err := d.loadUsb() + if err != nil { + return nil, err + } + + runConf := deviceConfig.RunConfig{} + runConf.PostHooks = []func() error{d.Register} + + for _, usb := range usbs { + if !usbIsOurDevice(d.config, &usb) { + continue + } + + runConf.USBDevice = append(runConf.USBDevice, []deviceConfig.RunConfigItem{ + {Key: "devName", Value: d.name}, + {Key: "hostBus", Value: fmt.Sprintf("%d", usb.BusNum)}, + {Key: "hostAddr", Value: fmt.Sprintf("%d", usb.DevNum)}, + }...) + } + + if d.isRequired() && len(runConf.Mounts) <= 0 { + return nil, fmt.Errorf("Required USB device not found") + } + + return &runConf, nil +} + // Stop is run when the device is removed from the instance. func (d *usb) Stop() (*deviceConfig.RunConfig, error) { - // Unregister any USB event handlers for this device. - usbUnregisterHandler(d.inst, d.name) - runConf := deviceConfig.RunConfig{ PostHooks: []func() error{d.postStop}, } - err := unixDeviceRemove(d.inst.DevicesPath(), "unix", d.name, "", &runConf) - if err != nil { - return nil, err + if d.inst.Type() == instancetype.Container { + // Unregister any USB event handlers for this device. + usbUnregisterHandler(d.inst, d.name) + + err := unixDeviceRemove(d.inst.DevicesPath(), "unix", d.name, "", &runConf) + if err != nil { + return nil, err + } } return &runConf, nil @@ -246,3 +284,10 @@ func (d *usb) loadRawValues(p string) (map[string]string, error) { return values, nil } + +// CanHotPlug returns true if attached to a container, otherwise false. It also +// returns a list of fields that can be updated without triggering a device remove & add (in this +// case an empty list). +// func (d *usb) CanHotPlug() (bool, []string) { +// return d.inst.Type() == instancetype.Container, []string{} +// } From c1a959117631fd0177c66c28fd1ad7f6cf1dd562 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 9 Oct 2020 16:38:45 +0200 Subject: [PATCH 6/7] lxd/instance/drivers: Add qemuUSBDev template This adds a device template for USB devices. Signed-off-by: Thomas Hipp --- lxd/instance/drivers/driver_qemu_templates.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lxd/instance/drivers/driver_qemu_templates.go b/lxd/instance/drivers/driver_qemu_templates.go index c1b383bc6b..f1f77eb231 100644 --- a/lxd/instance/drivers/driver_qemu_templates.go +++ b/lxd/instance/drivers/driver_qemu_templates.go @@ -508,3 +508,12 @@ addr = "{{.devAddr}}" multifunction = "on" {{- end }} `)) + +var qemuUSBDev = template.Must(template.New("qemuUSBDev").Parse(` +# USB host device ("{{.devName}}" device) +[device "dev-lxd_{{.devName}}"] +driver = "usb-host" +bus = "qemu_usb.0" +hostaddr = "{{.hostAddr}}" +hostbus = "{{.hostBus}}" +`)) From b45f0198c757029860340522eef3443e3a71f376 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 9 Oct 2020 16:39:41 +0200 Subject: [PATCH 7/7] lxd/instance/drivers: Add USB devices to qemu config This adds configured USB devices to the qemu config file. Signed-off-by: Thomas Hipp --- lxd/instance/drivers/driver_qemu.go | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 0b697cbf58..0d17257419 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -1842,6 +1842,14 @@ func (vm *qemu) generateQemuConfigFile(busName string, devConfs []*deviceConfig. return "", err } } + + // Add USB device. + if len(runConf.USBDevice) > 0 { + err = vm.addUSBDeviceConfig(sb, bus, runConf.USBDevice) + if err != nil { + return "", err + } + } } // Write the agent mount config. @@ -2256,6 +2264,47 @@ func (vm *qemu) addGPUDevConfig(sb *strings.Builder, bus *qemuBus, gpuConfig []d return nil } +func (vm *qemu) addUSBDeviceConfig(sb *strings.Builder, bus *qemuBus, usbConfig []deviceConfig.RunConfigItem) error { + var devName, hostBus, hostAddr string + + for _, usbItem := range usbConfig { + if usbItem.Key == "devName" { + devName = usbItem.Value + } else if usbItem.Key == "hostBus" { + hostBus = usbItem.Value + } else if usbItem.Key == "hostAddr" { + hostAddr = usbItem.Value + } + } + + tplFields := map[string]interface{}{ + "hostBus": hostBus, + "hostAddr": hostAddr, + "devName": devName, + } + + // Add main GPU device in VGA mode to qemu config. + err := qemuUSBDev.Execute(sb, tplFields) + if err != nil { + return err + } + + hostBusInt, err := strconv.Atoi(hostBus) + if err != nil { + return err + } + + hostAddrInt, err := strconv.Atoi(hostAddr) + if err != nil { + return err + } + + // Add path to devPaths. This way, the path will be included in the apparmor profile. + vm.devPaths = append(vm.devPaths, fmt.Sprintf("/dev/bus/usb/%03d/%03d", hostBusInt, hostAddrInt)) + + return nil +} + // pidFilePath returns the path where the qemu process should write its PID. func (vm *qemu) pidFilePath() string { return filepath.Join(vm.LogPath(), "qemu.pid") From lxc-bot at linuxcontainers.org Fri Oct 9 15:15:54 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Fri, 09 Oct 2020 08:15:54 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/events: Handle default permissiosn in projects Message-ID: <5f807eaa.1c69fb81.eeb32.3eeaSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From a4808d30eec836729125ca6378f076a34e46a842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 9 Oct 2020 11:15:10 -0400 Subject: [PATCH] lxd/events: Handle default permissiosn in projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/events.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lxd/events.go b/lxd/events.go index 1c58ce99db..80b2378dbd 100644 --- a/lxd/events.go +++ b/lxd/events.go @@ -13,6 +13,7 @@ import ( ) var eventTypes = []string{"logging", "operation", "lifecycle"} +var privilegedEventTypes = []string{"logging"} var eventsCmd = APIEndpoint{ Path: "events", @@ -37,7 +38,14 @@ func eventsSocket(d *Daemon, r *http.Request, w http.ResponseWriter) error { project := projectParam(r) types := strings.Split(r.FormValue("type"), ",") if len(types) == 1 && types[0] == "" { - types = eventTypes + types = []string{} + for _, entry := range eventTypes { + if !d.userIsAdmin(r) && shared.StringInSlice(entry, privilegedEventTypes) { + continue + } + + types = append(types, entry) + } } // Validate event types. From lxc-bot at linuxcontainers.org Fri Oct 9 16:21:54 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Fri, 09 Oct 2020 09:21:54 -0700 (PDT) Subject: [lxc-devel] [lxd/master] dnsmasq: Adds 100ms sleep to successful Kill() to allow sockets to be released by OS Message-ID: <5f808e22.1c69fb81.1de56.92e1SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 453 bytes Desc: not available URL: -------------- next part -------------- From 23694b750d164369ece64c402f36a76358d2d957 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 17:19:44 +0100 Subject: [PATCH] lxd/dnsmasq: Adds 100ms sleep to successful Kill() to allow sockets to be released by OS Otherwise trying to immediately start dnsmasq again can result in socket binding errors. Signed-off-by: Thomas Parrott --- lxd/dnsmasq/dnsmasq.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lxd/dnsmasq/dnsmasq.go b/lxd/dnsmasq/dnsmasq.go index 90c222ae66..d8491c2572 100644 --- a/lxd/dnsmasq/dnsmasq.go +++ b/lxd/dnsmasq/dnsmasq.go @@ -8,6 +8,7 @@ import ( "os" "strings" "sync" + "time" "github.com/lxc/lxd/lxd/project" "github.com/lxc/lxd/shared" @@ -95,7 +96,8 @@ func Kill(name string, reload bool) error { return fmt.Errorf("Unable to kill dnsmasq: %s", err) } - // Cleanup + time.Sleep(100 * time.Millisecond) // Give OS time to release sockets. + return nil } From lxc-bot at linuxcontainers.org Fri Oct 9 18:45:41 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Fri, 09 Oct 2020 11:45:41 -0700 (PDT) Subject: [lxc-devel] [lxd/master] VM: Fixes disk resize regression Message-ID: <5f80afd5.1c69fb81.acc97.cee0SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 552 bytes Desc: not available URL: -------------- next part -------------- From 57f1dc94a63d5faf175b096f280269adcca51786 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 19:39:09 +0100 Subject: [PATCH 1/2] lxd/instance/drivers/driver/qemu: Restores ability to resize VM disks - Moves check for preventing update of devices when VM is running into updateDevices(). - Fixes regression introduced by https://github.com/lxc/lxd/commit/6697599975c1199c226c00b68c7e3e278ea49c66#diff-8ac4860b5f669c17c92c37545fe2ecfd. - Removes 1 call to IsRunning() by passing into updateDevices() from Update(). Signed-off-by: Thomas Parrott --- lxd/instance/drivers/driver_qemu.go | 44 +++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 0b697cbf58..7c641ef033 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -2836,7 +2836,37 @@ func (vm *qemu) Update(args db.InstanceArgs, userRequested bool) error { } // Diff the devices. - removeDevices, addDevices, updateDevices, allUpdatedKeys := oldExpandedDevices.Update(vm.expandedDevices, nil) + removeDevices, addDevices, updateDevices, allUpdatedKeys := oldExpandedDevices.Update(vm.expandedDevices, func(oldDevice deviceConfig.Device, newDevice deviceConfig.Device) []string { + // This function needs to return a list of fields that are excluded from differences + // 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". + oldNICType, err := nictype.NICType(vm.state, vm.Project(), newDevice) + if err != nil { + return []string{} // Cannot hot-update due to config error. + } + + newNICType, err := nictype.NICType(vm.state, vm.Project(), oldDevice) + if err != nil { + return []string{} // Cannot hot-update due to config error. + } + + if oldDevice["type"] != newDevice["type"] || oldNICType != newNICType { + return []string{} // Device types aren't the same, so this cannot be an update. + } + + d, err := device.New(vm, vm.state, "", newDevice, nil, nil) + if err != nil { + return []string{} // Couldn't create Device, so this cannot be an update. + } + + // TODO modify device framework to differentiate between devices that can only be updated when VM + // is stopped, but don't need to be removed then re-added. For now we rely upon the disk device + // indicating that it supports hot plugging, even for VMs, which it cannot. However this is + // needed so that the disk device's Update() function is called to allow disk resizing. + _, updateFields := d.CanHotPlug() + return updateFields + }) if userRequested { // Do some validation of the config diff. @@ -2854,12 +2884,8 @@ func (vm *qemu) Update(args db.InstanceArgs, userRequested bool) error { isRunning := vm.IsRunning() - if isRunning && len(allUpdatedKeys) > 0 { - return fmt.Errorf("Devices cannot be changed when VM is running") - } - // Use the device interface to apply update changes. - err = vm.updateDevices(removeDevices, addDevices, updateDevices, oldExpandedDevices) + err = vm.updateDevices(removeDevices, addDevices, updateDevices, oldExpandedDevices, isRunning) if err != nil { return err } @@ -3055,8 +3081,10 @@ func (vm *qemu) updateMemoryLimit(newLimit string) error { return fmt.Errorf("Failed setting memory to %d bytes (currently %d bytes) as it was taking too long", newSizeBytes, curSizeBytes) } -func (vm *qemu) updateDevices(removeDevices deviceConfig.Devices, addDevices deviceConfig.Devices, updateDevices deviceConfig.Devices, oldExpandedDevices deviceConfig.Devices) error { - isRunning := vm.IsRunning() +func (vm *qemu) updateDevices(removeDevices deviceConfig.Devices, addDevices deviceConfig.Devices, updateDevices deviceConfig.Devices, oldExpandedDevices deviceConfig.Devices, isRunning bool) error { + if isRunning && (len(removeDevices) > 0 || len(addDevices) > 0 || len(updateDevices) > 0) { + return fmt.Errorf("Devices cannot be changed when VM is running") + } // Remove devices in reverse order to how they were added. for _, dev := range removeDevices.Reversed() { From 68dde3234a2e89118806ee57f60e8f4cd04ef997 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 19:44:42 +0100 Subject: [PATCH 2/2] lxd/device/disk: Adds comment about VM instances depending on CanHotPlug fields for stopped disk resize Signed-off-by: Thomas Parrott --- lxd/device/disk.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lxd/device/disk.go b/lxd/device/disk.go index 790618cc4a..4f728007e7 100644 --- a/lxd/device/disk.go +++ b/lxd/device/disk.go @@ -231,6 +231,8 @@ func (d *disk) validateEnvironment() error { // CanHotPlug returns whether the device can be managed whilst the instance is running, it also // returns a list of fields that can be updated without triggering a device remove & add. +// Note: At current time VM instances rely on this function indicating live update of "size" field is possible +// to allow disk resize when VM is stopped. func (d *disk) CanHotPlug() (bool, []string) { return true, []string{"limits.max", "limits.read", "limits.write", "size"} } From lxc-bot at linuxcontainers.org Fri Oct 9 20:40:55 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Fri, 09 Oct 2020 13:40:55 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/instance/qemu: Fix bad event name Message-ID: <5f80cad7.1c69fb81.ed318.c6fbSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From f9e475d788dd3ace373713795bf3cb567de85b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 9 Oct 2020 13:36:20 -0400 Subject: [PATCH] lxd/instance/qemu: Fix bad event name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/instance/drivers/driver_qemu.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 7c641ef033..5aebe9509c 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -623,7 +623,7 @@ func (vm *qemu) Shutdown(timeout time.Duration) error { } op.Done(nil) - vm.state.Events.SendLifecycle(vm.project, "instance-shutdown", fmt.Sprintf("/1.0/virtual-machines/%s", vm.name), nil) + vm.state.Events.SendLifecycle(vm.project, "virtual-machine-shutdown", fmt.Sprintf("/1.0/virtual-machines/%s", vm.name), nil) return nil } From lxc-bot at linuxcontainers.org Fri Oct 9 20:55:26 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Fri, 09 Oct 2020 13:55:26 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/storage: Check base image is available locally Message-ID: <5f80ce3e.1c69fb81.9aa45.cd93SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 471 bytes Desc: not available URL: -------------- next part -------------- From e035cf76c534740d806556dbb7df0b6532a14274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 9 Oct 2020 16:51:10 -0400 Subject: [PATCH] lxd/storage: Check base image is available locally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This uses a bit of a shortcut by checking the filesystem for it instead of hitting the database. Closes #8015 Signed-off-by: Stéphane Graber --- lxd/storage/backend_lxd.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index 379c644874..20ddfc145c 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -1125,12 +1125,17 @@ func (b *lxdBackend) CreateInstanceFromMigration(inst instance.Instance, conn io // transfer the base image files too. if args.MigrationType.FSType == migration.MigrationFSType_RSYNC { fingerprint := inst.ExpandedConfig()["volatile.base_image"] + + // Confirm that the image is present in the project. _, _, err = b.state.Cluster.GetImage(inst.Project(), fingerprint, false) if err != db.ErrNoSuchObject && err != nil { return err } - if err == nil { + // Then make sure that the image is available locally too (not guaranteed in clusters). + local := shared.PathExists(shared.VarPath("images", fingerprint)) + + if err == nil && local { logger.Debug("Using optimised migration from existing image", log.Ctx{"fingerprint": fingerprint}) // Populate the volume filler with the fingerprint and image filler From lxc-bot at linuxcontainers.org Sun Oct 11 19:14:31 2020 From: lxc-bot at linuxcontainers.org (AndrewElvisDeng on Github) Date: Sun, 11 Oct 2020 12:14:31 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Fix thinpool typo Message-ID: <5f835997.1c69fb81.496f9.86d6SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 473 bytes Desc: not available URL: -------------- next part -------------- From 47d218473ca0ecf9fd8af16bad056c5549a018f6 Mon Sep 17 00:00:00 2001 From: Andrew Deng Date: Fri, 2 Oct 2020 15:46:30 -0500 Subject: [PATCH 1/3] fixed typo in storage_pools.go --- lxd/db/storage_pools.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go index e27a3d1e12..76d12693d1 100644 --- a/lxd/db/storage_pools.go +++ b/lxd/db/storage_pools.go @@ -885,7 +885,7 @@ var StoragePoolNodeConfigKeys = []string{ "source", "volatile.initial_source", "zfs.pool_name", - "lvm.thinpool", + "lvm.thinpool_name", "lvm.vg_name", } From 24f0a70f543ae88bce0eb4f3e021081f400dc90d Mon Sep 17 00:00:00 2001 From: Andrew Deng Date: Fri, 2 Oct 2020 17:30:49 -0500 Subject: [PATCH 2/3] initial patch fix implementation done --- lxd/patches.go | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/lxd/patches.go b/lxd/patches.go index 170976700f..54967ecffd 100644 --- a/lxd/patches.go +++ b/lxd/patches.go @@ -101,6 +101,7 @@ var patches = []patch{ {name: "clustering_drop_database_role", stage: patchPostDaemonStorage, run: patchClusteringDropDatabaseRole}, {name: "network_clear_bridge_volatile_hwaddr", stage: patchPostDaemonStorage, run: patchNetworkCearBridgeVolatileHwaddr}, {name: "move_backups_instances", stage: patchPostDaemonStorage, run: patchMoveBackupsInstances}, + {name: "TEMPNAME", stage: TEMP, run: patchTEMPNAME}, } type patch struct { @@ -165,6 +166,68 @@ func patchesApply(d *Daemon, stage patchStage) error { // Patches begin here +// +func patchTEMPNAME(name string, d *Daemon) error { + tx, err := d.cluster.Begin() + if err != nil { + return errors.Wrap(err, "failed to begin transaction") + } + + // 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 { + // Fetch the config for this lvm pool and check if it has the + // lvn.thinpool_name key. + config, err := query.SelectConfig( + tx, "storage_pools_config", "storage_pool_id=?", poolID) + if err != nil { + return errors.Wrap(err, "failed a fetch of lvm pool config") + } + + value, ok := "lvm.thinpool" + if !ok { + continue + } + + // Delete the current key + _, err = tx.Exec(` + DELETE FROM storage_pools_config WHERE key='lvm.thinpool' AND storage_pool_id=?`, poolID) + if err != nil { + return errors.Wrapf(err, "failed to delete %s config", key) + } + + // Add the config entry for each node + for _, nodeID := range nodeIDs { + _, err := tx.Exec(` + INSERT INTO storage_pools_config(storage_pool_id, node_id, key, value) + VALUES(?, ?, 'lvm.thinpool_name', ?) + `, poolID, curNodeID, value) + if err != nil { + return errors.Wrapf(err, "failed to create %s node config", key) + } + } + } + } + + err = tx.Commit() + if err != nil { + return errors.Wrap(err, "failed to commit transaction") + } + + return err + +} + // Moves backups from shared.VarPath("backups") to shared.VarPath("backups", "instances"). func patchMoveBackupsInstances(name string, d *Daemon) error { if !shared.PathExists(shared.VarPath("backups")) { From 133825f12eabc5c8fef70049d8504a1c6ba6b908 Mon Sep 17 00:00:00 2001 From: Andrew Deng Date: Sun, 11 Oct 2020 14:10:11 -0500 Subject: [PATCH 3/3] renamed functions, comments, adhered to patches.go's style --- lxd/patches.go | 128 ++++++++++++++++++++++++------------------------- 1 file changed, 63 insertions(+), 65 deletions(-) diff --git a/lxd/patches.go b/lxd/patches.go index 54967ecffd..25d7af4d1d 100644 --- a/lxd/patches.go +++ b/lxd/patches.go @@ -101,7 +101,7 @@ var patches = []patch{ {name: "clustering_drop_database_role", stage: patchPostDaemonStorage, run: patchClusteringDropDatabaseRole}, {name: "network_clear_bridge_volatile_hwaddr", stage: patchPostDaemonStorage, run: patchNetworkCearBridgeVolatileHwaddr}, {name: "move_backups_instances", stage: patchPostDaemonStorage, run: patchMoveBackupsInstances}, - {name: "TEMPNAME", stage: TEMP, run: patchTEMPNAME}, + {name: "thinpool_typo_fix", stage: patchPostDaemonStorage, run: patchThinpoolTypoFix}, } type patch struct { @@ -164,70 +164,6 @@ func patchesApply(d *Daemon, stage patchStage) error { return nil } -// Patches begin here - -// -func patchTEMPNAME(name string, d *Daemon) error { - tx, err := d.cluster.Begin() - if err != nil { - return errors.Wrap(err, "failed to begin transaction") - } - - // 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 { - // Fetch the config for this lvm pool and check if it has the - // lvn.thinpool_name key. - config, err := query.SelectConfig( - tx, "storage_pools_config", "storage_pool_id=?", poolID) - if err != nil { - return errors.Wrap(err, "failed a fetch of lvm pool config") - } - - value, ok := "lvm.thinpool" - if !ok { - continue - } - - // Delete the current key - _, err = tx.Exec(` - DELETE FROM storage_pools_config WHERE key='lvm.thinpool' AND storage_pool_id=?`, poolID) - if err != nil { - return errors.Wrapf(err, "failed to delete %s config", key) - } - - // Add the config entry for each node - for _, nodeID := range nodeIDs { - _, err := tx.Exec(` - INSERT INTO storage_pools_config(storage_pool_id, node_id, key, value) - VALUES(?, ?, 'lvm.thinpool_name', ?) - `, poolID, curNodeID, value) - if err != nil { - return errors.Wrapf(err, "failed to create %s node config", key) - } - } - } - } - - err = tx.Commit() - if err != nil { - return errors.Wrap(err, "failed to commit transaction") - } - - return err - -} - // Moves backups from shared.VarPath("backups") to shared.VarPath("backups", "instances"). func patchMoveBackupsInstances(name string, d *Daemon) error { if !shared.PathExists(shared.VarPath("backups")) { @@ -3777,3 +3713,65 @@ func patchUpdateFromV30(_ *sql.Tx) error { return nil } + +//renames any config incorrectly set config file entries due to the lvm.thinpool_name typo +func patchThinpoolTypoFix(name string, d *Daemon) error { + tx, err := d.cluster.Begin() + if err != nil { + return errors.Wrap(err, "failed to begin transaction") + } + + // 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 { + // Fetch the config for this lvm pool and check if it has the + // lvn.thinpool_name key. + config, err := query.SelectConfig( + tx, "storage_pools_config", "storage_pool_id=?", poolID) + if err != nil { + return errors.Wrap(err, "failed a fetch of lvm pool config") + } + + value, ok := "lvm.thinpool" + if !ok { + continue + } + + // Delete the current key + _, err = tx.Exec(` + DELETE FROM storage_pools_config WHERE key='lvm.thinpool' AND storage_pool_id=?`, poolID) + if err != nil { + return errors.Wrapf(err, "failed to delete %s config", key) + } + + // Add the config entry for each node + for _, nodeID := range nodeIDs { + _, err := tx.Exec(` + INSERT INTO storage_pools_config(storage_pool_id, node_id, key, value) + VALUES(?, ?, 'lvm.thinpool_name', ?) + `, poolID, curNodeID, value) + if err != nil { + return errors.Wrapf(err, "failed to create %s node config", key) + } + } + } + } + + err = tx.Commit() + if err != nil { + return errors.Wrap(err, "failed to commit transaction") + } + + return err + +} \ No newline at end of file From lxc-bot at linuxcontainers.org Sun Oct 11 19:53:45 2020 From: lxc-bot at linuxcontainers.org (AndrewElvisDeng on Github) Date: Sun, 11 Oct 2020 12:53:45 -0700 (PDT) Subject: [lxc-devel] [lxd/master] fixed typo, created patch Message-ID: <5f8362c9.1c69fb81.f5dc6.91a0SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 525 bytes Desc: not available URL: -------------- next part -------------- From b2df1fc1618355a26659724041613a4f19f43260 Mon Sep 17 00:00:00 2001 From: Andrew Deng Date: Sun, 11 Oct 2020 14:51:29 -0500 Subject: [PATCH] fixed typo, created patch Signed-off-by: Andrew Deng --- lxd/db/storage_pools.go | 2 +- lxd/patches.go | 65 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go index e27a3d1e12..76d12693d1 100644 --- a/lxd/db/storage_pools.go +++ b/lxd/db/storage_pools.go @@ -885,7 +885,7 @@ var StoragePoolNodeConfigKeys = []string{ "source", "volatile.initial_source", "zfs.pool_name", - "lvm.thinpool", + "lvm.thinpool_name", "lvm.vg_name", } diff --git a/lxd/patches.go b/lxd/patches.go index 170976700f..9ae66d6af1 100644 --- a/lxd/patches.go +++ b/lxd/patches.go @@ -101,6 +101,7 @@ var patches = []patch{ {name: "clustering_drop_database_role", stage: patchPostDaemonStorage, run: patchClusteringDropDatabaseRole}, {name: "network_clear_bridge_volatile_hwaddr", stage: patchPostDaemonStorage, run: patchNetworkCearBridgeVolatileHwaddr}, {name: "move_backups_instances", stage: patchPostDaemonStorage, run: patchMoveBackupsInstances}, + {name: "thinpool_typo_fix", stage: patchPostDaemonStorage, run: patchThinpoolTypoFix}, } type patch struct { @@ -163,8 +164,6 @@ func patchesApply(d *Daemon, stage patchStage) error { return nil } -// Patches begin here - // Moves backups from shared.VarPath("backups") to shared.VarPath("backups", "instances"). func patchMoveBackupsInstances(name string, d *Daemon) error { if !shared.PathExists(shared.VarPath("backups")) { @@ -3714,3 +3713,65 @@ func patchUpdateFromV30(_ *sql.Tx) error { return nil } + +// renames any config incorrectly set config file entries due to the lvm.thinpool_name typo +func patchThinpoolTypoFix(name string, d *Daemon) error { + tx, err := d.cluster.Begin() + if err != nil { + return errors.Wrap(err, "failed to begin transaction") + } + + // 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 { + // Fetch the config for this lvm pool and check if it has the + // lvn.thinpool_name key. + config, err := query.SelectConfig( + tx, "storage_pools_config", "storage_pool_id=?", poolID) + if err != nil { + return errors.Wrap(err, "failed a fetch of lvm pool config") + } + + value, ok := "lvm.thinpool" + if !ok { + continue + } + + // Delete the current key + _, err = tx.Exec(` + DELETE FROM storage_pools_config WHERE key='lvm.thinpool' AND storage_pool_id=?`, poolID) + if err != nil { + return errors.Wrapf(err, "failed to delete %s config", key) + } + + // Add the config entry for each node + for _, nodeID := range nodeIDs { + _, err := tx.Exec(` + INSERT INTO storage_pools_config(storage_pool_id, node_id, key, value) + VALUES(?, ?, 'lvm.thinpool_name', ?) + `, poolID, curNodeID, value) + if err != nil { + return errors.Wrapf(err, "failed to create %s node config", key) + } + } + } + } + + err = tx.Commit() + if err != nil { + return errors.Wrap(err, "failed to commit transaction") + } + + return err + +} \ No newline at end of file From lxc-bot at linuxcontainers.org Mon Oct 12 11:11:08 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Mon, 12 Oct 2020 04:11:08 -0700 (PDT) Subject: [lxc-devel] [go-lxc/v2] Fixes crash in cgroupItem() Message-ID: <5f8439cc.1c69fb81.f2d4b.424dSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 27422 bytes Desc: not available URL: -------------- next part -------------- From 5dd5308fffcf4c9713bdf392fefe68bbaca1157f Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 12 Oct 2020 12:09:50 +0100 Subject: [PATCH 1/2] container: Fixes crash in cgroupItem() Signed-off-by: Thomas Parrott --- container.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/container.go b/container.go index 7a9c5d5..caa2d72 100644 --- a/container.go +++ b/container.go @@ -838,6 +838,10 @@ func (c *Container) RunningConfigItem(key string) []string { } func (c *Container) cgroupItem(key string) []string { + if c.container == nil { + return nil + } + ckey := C.CString(key) defer C.free(unsafe.Pointer(ckey)) From 9e65e12275c754b980fc5d81da0771d1b04b0e28 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 12 Oct 2020 12:10:02 +0100 Subject: [PATCH 2/2] options: go fmt Signed-off-by: Thomas Parrott --- options.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/options.go b/options.go index 060abd5..08a64eb 100644 --- a/options.go +++ b/options.go @@ -107,12 +107,11 @@ type TemplateOptions struct { ExtraArgs []string } - type BackendStoreSpecs struct { FSType string FSSize uint64 - Dir *string - ZFS struct { + Dir *string + ZFS struct { Root string } LVM struct { @@ -123,7 +122,6 @@ type BackendStoreSpecs struct { } } - // DownloadTemplateOptions is a convenient set of options for "download" template. var DownloadTemplateOptions = TemplateOptions{ Template: "download", From lxc-bot at linuxcontainers.org Mon Oct 12 15:27:53 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Mon, 12 Oct 2020 08:27:53 -0700 (PDT) Subject: [lxc-devel] [lxc-ci/master] bin/test-lxd-ovn: Reorders comms tests so that external is last Message-ID: <5f8475f9.1c69fb81.16337.8a77SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 469 bytes Desc: not available URL: -------------- next part -------------- From 1b643e93288ed271f59146ea43ab7aa2221fa7c1 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 12 Oct 2020 16:26:41 +0100 Subject: [PATCH] bin/test-lxd-ovn: Reorders comms tests so that external is last Adds OVN to lxdbr0 test before external test, so we can see where intermittent failures are occurring. Signed-off-by: Thomas Parrott --- bin/test-lxd-ovn | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/bin/test-lxd-ovn b/bin/test-lxd-ovn index e8261ea..318dc06 100755 --- a/bin/test-lxd-ovn +++ b/bin/test-lxd-ovn @@ -96,10 +96,6 @@ echo "==> lxdbr0 to OVN gateway" lxc exec u1 -- ping -c1 -4 10.10.10.200 lxc exec u1 -- ping -c1 -6 fd42:4242:4242:1010::200 -echo "==> OVN to internet" -lxc exec u2 -- ping -c1 -4 linuxcontainers.org -lxc exec u2 -- ping -c1 -6 linuxcontainers.org - echo "==> OVN to OVN" lxc exec u2 -- ping -c1 -4 "${U3_IPV4}" lxc exec u2 -- ping -c1 -6 "${U3_IPV6}" @@ -112,6 +108,14 @@ echo "==> DNS resolution on OVN" lxc exec u3 -- ping -c1 -4 u2.lxd lxc exec u3 -- ping -c1 -6 u2.lxd +echo "==> OVN to lxdbr0" +lxc exec u2 -- ping -c1 10.10.10.1 +lxc exec u2 -- ping -c1 fd42:4242:4242:1010::1 + +echo "==> OVN to internet" +lxc exec u2 -- ping -c1 -4 linuxcontainers.org +lxc exec u2 -- ping -c1 -6 linuxcontainers.org + echo "===> Testing project restrictions" lxc project create testovn -c features.networks=true -c restricted=true @@ -168,10 +172,6 @@ echo "==> lxdbr0 to OVN gateway in project testovn" lxc exec u1 --project default -- ping -c1 -4 10.10.10.201 lxc exec u1 --project default -- ping -c1 -6 fd42:4242:4242:1010::201 -echo "==> OVN to internet in project testovn" -lxc exec u2 -- ping -c1 -4 linuxcontainers.org -lxc exec u2 -- ping -c1 -6 linuxcontainers.org - echo "==> OVN to OVN in project testovn" lxc exec u2 -- ping -c1 -4 "${U3_IPV4}" lxc exec u2 -- ping -c1 -6 "${U3_IPV6}" @@ -184,6 +184,14 @@ echo "==> DNS resolution on OVN in project testovn" lxc exec u3 -- ping -c1 -4 u2.lxd lxc exec u3 -- ping -c1 -6 u2.lxd +echo "==> OVN to lxdbr0 in project testovn" +lxc exec u2 -- ping -c1 10.10.10.1 +lxc exec u2 -- ping -c1 fd42:4242:4242:1010::1 + +echo "==> OVN to internet in project testovn" +lxc exec u2 -- ping -c1 -4 linuxcontainers.org +lxc exec u2 -- ping -c1 -6 linuxcontainers.org + echo "===> Check network in use protection from deletion" # Delete instances in default project first. lxc delete -f u1 u2 u3 --project default From lxc-bot at linuxcontainers.org Mon Oct 12 16:45:48 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Mon, 12 Oct 2020 09:45:48 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Storage: Don't remove empty LVM thinpool and volume group if lvm.vg.force_reuse enabled Message-ID: <5f84883c.1c69fb81.d0ecd.d77eSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 361 bytes Desc: not available URL: -------------- next part -------------- From 54922c2dd5ef61454df93b4535f6c6d4be0f42ce Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 12 Oct 2020 17:43:48 +0100 Subject: [PATCH] lxd/storage/drivers/driver/lvm: Don't remove empty thinpool and volume group if lvm.vg.force_reuse enabled Signed-off-by: Thomas Parrott --- lxd/storage/drivers/driver_lvm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage/drivers/driver_lvm.go b/lxd/storage/drivers/driver_lvm.go index f68d537cd8..cda510284f 100644 --- a/lxd/storage/drivers/driver_lvm.go +++ b/lxd/storage/drivers/driver_lvm.go @@ -333,7 +333,7 @@ func (d *lvm) Delete(op *operations.Operation) error { } removeVg := false - if vgExists { + if vgExists && !shared.IsTrue(d.config["lvm.vg.force_reuse"]) { // Count normal and thin volumes. lvCount, err := d.countLogicalVolumes(d.config["lvm.vg_name"]) if err != nil && err != errLVMNotFound { From lxc-bot at linuxcontainers.org Tue Oct 13 05:22:52 2020 From: lxc-bot at linuxcontainers.org (gsquire on Github) Date: Mon, 12 Oct 2020 22:22:52 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Fix Defer Message-ID: <5f8539ac.1c69fb81.f6cd1.f3f9SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 416 bytes Desc: not available URL: -------------- next part -------------- From 601d274f2d6fc935ac73d1e741c1ce2a570ab207 Mon Sep 17 00:00:00 2001 From: Garrett Squire Date: Mon, 12 Oct 2020 22:21:36 -0700 Subject: [PATCH] Fix defer in for loop This patch fixes a call to defer inside of a loop and instead uses an anonymous function to ensure proper behavior. --- lxd-agent/templates.go | 70 ++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/lxd-agent/templates.go b/lxd-agent/templates.go index 8a2833d892..2ce6a6a252 100644 --- a/lxd-agent/templates.go +++ b/lxd-agent/templates.go @@ -35,51 +35,53 @@ func templatesApply(path string) ([]string, error) { // Go through the files and copy them into place. files := []string{} for tplPath, tpl := range metadata.Templates { - filePath := filepath.Join(path, fmt.Sprintf("%s.out", tpl.Template)) + func() { + filePath := filepath.Join(path, fmt.Sprintf("%s.out", tpl.Template)) - if !shared.PathExists(filePath) { - continue - } - - var w *os.File - if shared.PathExists(tplPath) { - if tpl.CreateOnly { + if !shared.PathExists(filePath) { continue } - // Open the existing file. - w, err = os.Create(tplPath) - if err != nil { - return nil, errors.Wrap(err, "Failed to create template file") + var w *os.File + if shared.PathExists(tplPath) { + if tpl.CreateOnly { + continue + } + + // Open the existing file. + w, err = os.Create(tplPath) + if err != nil { + return nil, errors.Wrap(err, "Failed to create template file") + } + } else { + // Create the directories leading to the file. + os.MkdirAll(filepath.Dir(tplPath), 0755) + + // Create the file itself. + w, err = os.Create(tplPath) + if err != nil { + return nil, err + } + + // Fix mode. + w.Chmod(0644) } - } else { - // Create the directories leading to the file. - os.MkdirAll(filepath.Dir(tplPath), 0755) + defer w.Close() - // Create the file itself. - w, err = os.Create(tplPath) + // Do the copy. + src, err := os.Open(filePath) if err != nil { return nil, err } + defer src.Close() - // Fix mode. - w.Chmod(0644) - } - defer w.Close() - - // Do the copy. - src, err := os.Open(filePath) - if err != nil { - return nil, err - } - defer src.Close() - - _, err = io.Copy(w, src) - if err != nil { - return nil, err - } + _, err = io.Copy(w, src) + if err != nil { + return nil, err + } - files = append(files, tplPath) + files = append(files, tplPath) + }() } return files, nil From lxc-bot at linuxcontainers.org Tue Oct 13 12:43:17 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Tue, 13 Oct 2020 05:43:17 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: OVN uplink terminology Message-ID: <5f85a0e5.1c69fb81.e4711.a94fSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 489 bytes Desc: not available URL: -------------- next part -------------- From 5d8ced58e9b060ef3638237f3b6ffeb150c65c23 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 09:38:36 +0100 Subject: [PATCH 1/8] shared/validate/validate: Removes inaccurate comments about optional values Signed-off-by: Thomas Parrott --- shared/validate/validate.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/shared/validate/validate.go b/shared/validate/validate.go index 10067d8c26..92939c4e39 100644 --- a/shared/validate/validate.go +++ b/shared/validate/validate.go @@ -161,7 +161,7 @@ func IsNetworkMAC(value string) error { return nil } -// IsNetworkAddress validates an IP (v4 or v6) address string. If string is empty, returns valid. +// IsNetworkAddress validates an IP (v4 or v6) address string. func IsNetworkAddress(value string) error { ip := net.ParseIP(value) if ip == nil { @@ -184,7 +184,7 @@ func IsNetworkAddressList(value string) error { return nil } -// IsNetworkV4 validates an IPv4 CIDR string. If string is empty, returns valid. +// IsNetworkV4 validates an IPv4 CIDR string. func IsNetworkV4(value string) error { ip, subnet, err := net.ParseCIDR(value) if err != nil { @@ -202,7 +202,7 @@ func IsNetworkV4(value string) error { return nil } -// IsNetworkAddressV4 validates an IPv4 addresss string. If string is empty, returns valid. +// IsNetworkAddressV4 validates an IPv4 addresss string. func IsNetworkAddressV4(value string) error { ip := net.ParseIP(value) if ip == nil || ip.To4() == nil { @@ -212,7 +212,7 @@ func IsNetworkAddressV4(value string) error { return nil } -// IsNetworkAddressCIDRV4 validates an IPv4 addresss string in CIDR format. If string is empty, returns valid. +// IsNetworkAddressCIDRV4 validates an IPv4 addresss string in CIDR format. func IsNetworkAddressCIDRV4(value string) error { ip, subnet, err := net.ParseCIDR(value) if err != nil { @@ -256,7 +256,7 @@ func IsNetworkV4List(value string) error { return nil } -// IsNetworkV6 validates an IPv6 CIDR string. If string is empty, returns valid. +// IsNetworkV6 validates an IPv6 CIDR string. func IsNetworkV6(value string) error { ip, subnet, err := net.ParseCIDR(value) if err != nil { @@ -274,7 +274,7 @@ func IsNetworkV6(value string) error { return nil } -// IsNetworkAddressV6 validates an IPv6 addresss string. If string is empty, returns valid. +// IsNetworkAddressV6 validates an IPv6 addresss string. func IsNetworkAddressV6(value string) error { ip := net.ParseIP(value) if ip == nil || ip.To4() != nil { @@ -284,7 +284,7 @@ func IsNetworkAddressV6(value string) error { return nil } -// IsNetworkAddressCIDRV6 validates an IPv6 addresss string in CIDR format. If string is empty, returns valid. +// IsNetworkAddressCIDRV6 validates an IPv6 addresss string in CIDR format. func IsNetworkAddressCIDRV6(value string) error { ip, subnet, err := net.ParseCIDR(value) if err != nil { From 6bc696e845f1e50f717363cb9b9191320452952a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 09:39:08 +0100 Subject: [PATCH 2/8] shared/validate/validate: Adds IsNetwork and IsNetworkList functions Signed-off-by: Thomas Parrott --- shared/validate/validate.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/shared/validate/validate.go b/shared/validate/validate.go index 92939c4e39..3978ad5a80 100644 --- a/shared/validate/validate.go +++ b/shared/validate/validate.go @@ -184,6 +184,32 @@ func IsNetworkAddressList(value string) error { return nil } +// IsNetwork validates an IP network CIDR string. +func IsNetwork(value string) error { + ip, subnet, err := net.ParseCIDR(value) + if err != nil { + return err + } + + if ip.String() != subnet.IP.String() { + return fmt.Errorf("Not an IP network address %q", value) + } + + return nil +} + +// IsNetworkList validates a comma delimited list of IP network CIDR strings. +func IsNetworkList(value string) error { + for _, network := range strings.Split(value, ",") { + err := IsNetwork(strings.TrimSpace(network)) + if err != nil { + return err + } + } + + return nil +} + // IsNetworkV4 validates an IPv4 CIDR string. func IsNetworkV4(value string) error { ip, subnet, err := net.ParseCIDR(value) From 654413bedc13dd4f2d48d886fe5855f9ca395aaf Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 09:41:09 +0100 Subject: [PATCH 3/8] shared/validate/validate: Re-orders IP validation functions So that IP family functions are together and in logical order (single item validator before list validators). Signed-off-by: Thomas Parrott --- shared/validate/validate.go | 120 ++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/shared/validate/validate.go b/shared/validate/validate.go index 3978ad5a80..2a84a2bf3e 100644 --- a/shared/validate/validate.go +++ b/shared/validate/validate.go @@ -228,6 +228,19 @@ func IsNetworkV4(value string) error { return nil } +// IsNetworkV4List validates a comma delimited list of IPv4 CIDR strings. +func IsNetworkV4List(value string) error { + for _, network := range strings.Split(value, ",") { + network = strings.TrimSpace(network) + err := IsNetworkV4(network) + if err != nil { + return err + } + } + + return nil +} + // IsNetworkAddressV4 validates an IPv4 addresss string. func IsNetworkAddressV4(value string) error { ip := net.ParseIP(value) @@ -238,6 +251,19 @@ func IsNetworkAddressV4(value string) error { return nil } +// IsNetworkAddressV4List validates a comma delimited list of IPv4 addresses. +func IsNetworkAddressV4List(value string) error { + for _, v := range strings.Split(value, ",") { + v = strings.TrimSpace(v) + err := IsNetworkAddressV4(v) + if err != nil { + return err + } + } + + return nil +} + // IsNetworkAddressCIDRV4 validates an IPv4 addresss string in CIDR format. func IsNetworkAddressCIDRV4(value string) error { ip, subnet, err := net.ParseCIDR(value) @@ -256,11 +282,15 @@ func IsNetworkAddressCIDRV4(value string) error { return nil } -// IsNetworkAddressV4List validates a comma delimited list of IPv4 addresses. -func IsNetworkAddressV4List(value string) error { - for _, v := range strings.Split(value, ",") { - v = strings.TrimSpace(v) - err := IsNetworkAddressV4(v) +// IsNetworkRangeV4 validates an IPv4 range in the format "start-end". +func IsNetworkRangeV4(value string) error { + ips := strings.SplitN(value, "-", 2) + if len(ips) != 2 { + return fmt.Errorf("IP range must contain start and end IP addresses") + } + + for _, ip := range ips { + err := IsNetworkAddressV4(ip) if err != nil { return err } @@ -269,11 +299,10 @@ func IsNetworkAddressV4List(value string) error { return nil } -// IsNetworkV4List validates a comma delimited list of IPv4 CIDR strings. -func IsNetworkV4List(value string) error { - for _, network := range strings.Split(value, ",") { - network = strings.TrimSpace(network) - err := IsNetworkV4(network) +// IsNetworkRangeV4List validates a comma delimited list of IPv4 ranges. +func IsNetworkRangeV4List(value string) error { + for _, ipRange := range strings.Split(value, ",") { + err := IsNetworkRangeV4(strings.TrimSpace(ipRange)) if err != nil { return err } @@ -300,31 +329,26 @@ func IsNetworkV6(value string) error { return nil } -// IsNetworkAddressV6 validates an IPv6 addresss string. -func IsNetworkAddressV6(value string) error { - ip := net.ParseIP(value) - if ip == nil || ip.To4() != nil { - return fmt.Errorf("Not an IPv6 address %q", value) +// IsNetworkV6List validates a comma delimited list of IPv6 CIDR strings. +func IsNetworkV6List(value string) error { + for _, network := range strings.Split(value, ",") { + network = strings.TrimSpace(network) + err := IsNetworkV6(network) + if err != nil { + return err + } } return nil } -// IsNetworkAddressCIDRV6 validates an IPv6 addresss string in CIDR format. -func IsNetworkAddressCIDRV6(value string) error { - ip, subnet, err := net.ParseCIDR(value) - if err != nil { - return err - } - - if ip.To4() != nil { +// IsNetworkAddressV6 validates an IPv6 addresss string. +func IsNetworkAddressV6(value string) error { + ip := net.ParseIP(value) + if ip == nil || ip.To4() != nil { return fmt.Errorf("Not an IPv6 address %q", value) } - if ip.String() == subnet.IP.String() { - return fmt.Errorf("Not a usable IPv6 address %q", value) - } - return nil } @@ -340,43 +364,19 @@ func IsNetworkAddressV6List(value string) error { return nil } -// IsNetworkV6List validates a comma delimited list of IPv6 CIDR strings. -func IsNetworkV6List(value string) error { - for _, network := range strings.Split(value, ",") { - network = strings.TrimSpace(network) - err := IsNetworkV6(network) - if err != nil { - return err - } - } - - return nil -} - -// IsNetworkRangeV4 validates an IPv4 range in the format "start-end". -func IsNetworkRangeV4(value string) error { - ips := strings.SplitN(value, "-", 2) - if len(ips) != 2 { - return fmt.Errorf("IP range must contain start and end IP addresses") +// IsNetworkAddressCIDRV6 validates an IPv6 addresss string in CIDR format. +func IsNetworkAddressCIDRV6(value string) error { + ip, subnet, err := net.ParseCIDR(value) + if err != nil { + return err } - for _, ip := range ips { - err := IsNetworkAddressV4(ip) - if err != nil { - return err - } + if ip.To4() != nil { + return fmt.Errorf("Not an IPv6 address %q", value) } - return nil -} - -// IsNetworkRangeV4List validates a comma delimited list of IPv4 ranges. -func IsNetworkRangeV4List(value string) error { - for _, ipRange := range strings.Split(value, ",") { - err := IsNetworkRangeV4(strings.TrimSpace(ipRange)) - if err != nil { - return err - } + if ip.String() == subnet.IP.String() { + return fmt.Errorf("Not a usable IPv6 address %q", value) } return nil From 7a900762a1978b06297a1870514337de45f77cef Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 10:14:08 +0100 Subject: [PATCH 4/8] lxd/device/nic/ovn: Comment Signed-off-by: Thomas Parrott --- lxd/device/nic_ovn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go index 71c77419d8..e499c7cf90 100644 --- a/lxd/device/nic_ovn.go +++ b/lxd/device/nic_ovn.go @@ -26,7 +26,7 @@ import ( type nicOVN struct { deviceCommon - network network.Network + network network.Network // Populated in validateConfig(). } // getIntegrationBridgeName returns the OVS integration bridge to use. From c34ee20298981bedd5d392dad010907e5001a218 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 10:29:29 +0100 Subject: [PATCH 5/8] doc/api-extensions: Removes mention of "parent" from projects_networks_restricted_uplinks feature Signed-off-by: Thomas Parrott --- doc/api-extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index bc3ee9e2d2..a2688afac2 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -1157,7 +1157,7 @@ Adds the `features.networks` config key to projects and the ability for a projec ## projects\_networks\_restricted\_uplinks Adds the `restricted.networks.uplinks` project config key to indicate (as a comma delimited list) which networks -the networks created inside the project can use as their uplink parent network. +the networks created inside the project can use as their uplink network. ## custom\_volume\_backup Add custom volume backup support. From 1b2dc8eba3c670b5794af94fad0226668bc95cf3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 10:29:55 +0100 Subject: [PATCH 6/8] doc/networks: Switch to "uplink" terminology for external OVN network access Signed-off-by: Thomas Parrott --- doc/networks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/networks.md b/doc/networks.md index 6df04b13c4..cc63de9c11 100644 --- a/doc/networks.md +++ b/doc/networks.md @@ -299,7 +299,7 @@ dns.search | string | - | - ipv4.address | string | standard mode | random unused subnet | IPv4 address for the bridge (CIDR notation). Use "none" to turn off IPv4 or "auto" to generate a new one ipv6.address | string | standard mode | random unused subnet | IPv6 address for the bridge (CIDR notation). Use "none" to turn off IPv6 or "auto" to generate a new one ipv6.dhcp.stateful | boolean | ipv6 dhcp | false | Whether to allocate addresses using DHCP -network | string | - | - | Parent network to use for outbound external network access +network | string | - | - | Uplink network to use for external network access ## network: physical From 6085a3da6a9ee8fb27b4e56fdd788165954b5b57 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 10:30:21 +0100 Subject: [PATCH 7/8] lxd/network/driver/ovn: Replace parent terminology with uplink Now that "uplink" concept is formalised in `restricted.networks.uplinks` we should use consistent terminilogy in code. Also "parent" concept is different and used for other meanings, so re-using when talking about uplink is confusing. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 400 +++++++++++++++++++------------------- 1 file changed, 200 insertions(+), 200 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 7387f10270..ca3373c9f4 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -29,11 +29,11 @@ import ( ) const ovnChassisPriorityMax = 32767 -const ovnVolatileParentIPv4 = "volatile.network.ipv4.address" -const ovnVolatileParentIPv6 = "volatile.network.ipv6.address" +const ovnVolatileUplinkIPv4 = "volatile.network.ipv4.address" +const ovnVolatileUplinkIPv6 = "volatile.network.ipv6.address" -// ovnParentVars OVN object variables derived from parent network. -type ovnParentVars struct { +// ovnUplinkVars OVN object variables derived from uplink network. +type ovnUplinkVars struct { // Router. routerExtPortIPv4Net string routerExtPortIPv6Net string @@ -48,10 +48,10 @@ type ovnParentVars struct { dnsIPv4 []net.IP } -// ovnParentPortBridgeVars parent bridge port variables used for start/stop. -type ovnParentPortBridgeVars struct { +// ovnUplinkPortBridgeVars uplink bridge port variables used for start/stop. +type ovnUplinkPortBridgeVars struct { ovsBridge string - parentEnd string + uplinkEnd string ovsEnd string } @@ -103,8 +103,8 @@ func (n *ovn) Validate(config map[string]string) error { "dns.search": validate.IsAny, // Volatile keys populated automatically as needed. - ovnVolatileParentIPv4: validate.Optional(validate.IsNetworkAddressV4), - ovnVolatileParentIPv6: validate.Optional(validate.IsNetworkAddressV6), + ovnVolatileUplinkIPv4: validate.Optional(validate.IsNetworkAddressV4), + ovnVolatileUplinkIPv6: validate.Optional(validate.IsNetworkAddressV6), } err := n.validate(config, rules) @@ -351,102 +351,102 @@ func (n *ovn) getIntSwitchInstancePortPrefix() string { return fmt.Sprintf("%s-instance", n.getNetworkPrefix()) } -// setupParentPort initialises the parent uplink connection. Returns the derived ovnParentVars settings used +// setupUplinkPort initialises the uplink connection. Returns the derived ovnUplinkVars settings used // during the initial creation of the logical network. -func (n *ovn) setupParentPort(routerMAC net.HardwareAddr) (*ovnParentVars, error) { - // Parent network must be in default project. - parentNet, err := LoadByName(n.state, project.Default, n.config["network"]) +func (n *ovn) setupUplinkPort(routerMAC net.HardwareAddr) (*ovnUplinkVars, error) { + // Uplink network must be in default project. + uplinkNet, err := LoadByName(n.state, project.Default, n.config["network"]) if err != nil { - return nil, errors.Wrapf(err, "Failed loading parent network %q", n.config["network"]) + return nil, errors.Wrapf(err, "Failed loading uplink network %q", n.config["network"]) } - switch parentNet.Type() { + switch uplinkNet.Type() { case "bridge": - return n.setupParentPortBridge(parentNet, routerMAC) + return n.setupUplinkPortBridge(uplinkNet, routerMAC) case "physical": - return n.setupParentPortPhysical(parentNet, routerMAC) + return n.setupUplinkPortPhysical(uplinkNet, routerMAC) } - return nil, fmt.Errorf("Failed setting up parent port, network type %q unsupported as OVN parent", parentNet.Type()) + return nil, fmt.Errorf("Failed setting up uplink port, network type %q unsupported as OVN uplink", uplinkNet.Type()) } -// setupParentPortBridge allocates external IPs on the parent bridge. -// Returns the derived ovnParentVars settings. -func (n *ovn) setupParentPortBridge(parentNet Network, routerMAC net.HardwareAddr) (*ovnParentVars, error) { - bridgeNet, ok := parentNet.(*bridge) +// setupUplinkPortBridge allocates external IPs on the uplink bridge. +// Returns the derived ovnUplinkVars settings. +func (n *ovn) setupUplinkPortBridge(uplinkNet Network, routerMAC net.HardwareAddr) (*ovnUplinkVars, error) { + bridgeNet, ok := uplinkNet.(*bridge) if !ok { return nil, fmt.Errorf("Network is not bridge type") } err := bridgeNet.checkClusterWideMACSafe(bridgeNet.config) if err != nil { - return nil, errors.Wrapf(err, "Network %q is not suitable for use as OVN parent", bridgeNet.name) + return nil, errors.Wrapf(err, "Network %q is not suitable for use as OVN uplink", bridgeNet.name) } - v, err := n.allocateParentPortIPs(parentNet, routerMAC) + v, err := n.allocateUplinkPortIPs(uplinkNet, routerMAC) if err != nil { - return nil, errors.Wrapf(err, "Failed allocating parent port IPs on network %q", parentNet.Name()) + return nil, errors.Wrapf(err, "Failed allocating uplink port IPs on network %q", uplinkNet.Name()) } return v, nil } -// setupParentPortPhysical allocates external IPs on the parent network. -// Returns the derived ovnParentVars settings. -func (n *ovn) setupParentPortPhysical(parentNet Network, routerMAC net.HardwareAddr) (*ovnParentVars, error) { - v, err := n.allocateParentPortIPs(parentNet, routerMAC) +// setupUplinkPortPhysical allocates external IPs on the uplink network. +// Returns the derived ovnUplinkVars settings. +func (n *ovn) setupUplinkPortPhysical(uplinkNet Network, routerMAC net.HardwareAddr) (*ovnUplinkVars, error) { + v, err := n.allocateUplinkPortIPs(uplinkNet, routerMAC) if err != nil { - return nil, errors.Wrapf(err, "Failed allocating parent port IPs on network %q", parentNet.Name()) + return nil, errors.Wrapf(err, "Failed allocating uplink port IPs on network %q", uplinkNet.Name()) } return v, nil } -// allocateParentPortIPs attempts to find a free IP in the parent network's OVN ranges and then stores it in -// ovnVolatileParentIPv4 and ovnVolatileParentIPv6 config keys on this network. Returns ovnParentVars settings. -func (n *ovn) allocateParentPortIPs(parentNet Network, routerMAC net.HardwareAddr) (*ovnParentVars, error) { - v := &ovnParentVars{} +// allocateUplinkPortIPs attempts to find a free IP in the uplink network's OVN ranges and then stores it in +// ovnVolatileUplinkIPv4 and ovnVolatileUplinkIPv6 config keys on this network. Returns ovnUplinkVars settings. +func (n *ovn) allocateUplinkPortIPs(uplinkNet Network, routerMAC net.HardwareAddr) (*ovnUplinkVars, error) { + v := &ovnUplinkVars{} - parentNetConf := parentNet.Config() + uplinkNetConf := uplinkNet.Config() - // Parent derived settings. - v.extSwitchProviderName = parentNet.Name() + // Uplink derived settings. + v.extSwitchProviderName = uplinkNet.Name() - // Detect parent gateway setting. - parentIPv4CIDR := parentNetConf["ipv4.address"] - if parentIPv4CIDR == "" { - parentIPv4CIDR = parentNetConf["ipv4.gateway"] + // Detect uplink gateway setting. + uplinkIPv4CIDR := uplinkNetConf["ipv4.address"] + if uplinkIPv4CIDR == "" { + uplinkIPv4CIDR = uplinkNetConf["ipv4.gateway"] } - parentIPv6CIDR := parentNetConf["ipv6.address"] - if parentIPv6CIDR == "" { - parentIPv6CIDR = parentNetConf["ipv6.gateway"] + uplinkIPv6CIDR := uplinkNetConf["ipv6.address"] + if uplinkIPv6CIDR == "" { + uplinkIPv6CIDR = uplinkNetConf["ipv6.gateway"] } - // Optional parent values. - parentIPv4, parentIPv4Net, err := net.ParseCIDR(parentIPv4CIDR) + // Optional uplink values. + uplinkIPv4, uplinkIPv4Net, err := net.ParseCIDR(uplinkIPv4CIDR) if err == nil { - v.dnsIPv4 = []net.IP{parentIPv4} - v.routerExtGwIPv4 = parentIPv4 + v.dnsIPv4 = []net.IP{uplinkIPv4} + v.routerExtGwIPv4 = uplinkIPv4 } - parentIPv6, parentIPv6Net, err := net.ParseCIDR(parentIPv6CIDR) + uplinkIPv6, uplinkIPv6Net, err := net.ParseCIDR(uplinkIPv6CIDR) if err == nil { - v.dnsIPv6 = []net.IP{parentIPv6} - v.routerExtGwIPv6 = parentIPv6 + v.dnsIPv6 = []net.IP{uplinkIPv6} + v.routerExtGwIPv6 = uplinkIPv6 } // Detect optional DNS server list. - if parentNetConf["dns.nameservers"] != "" { + if uplinkNetConf["dns.nameservers"] != "" { // Reset nameservers. v.dnsIPv4 = nil v.dnsIPv6 = nil - nsList := strings.Split(parentNetConf["dns.nameservers"], ",") + nsList := strings.Split(uplinkNetConf["dns.nameservers"], ",") for _, ns := range nsList { nsIP := net.ParseIP(strings.TrimSpace(ns)) if nsIP == nil { - return nil, fmt.Errorf("Invalid parent nameserver") + return nil, fmt.Errorf("Invalid uplink nameserver") } if nsIP.To4() == nil { @@ -457,63 +457,63 @@ func (n *ovn) allocateParentPortIPs(parentNet Network, routerMAC net.HardwareAdd } } - // Parse existing allocated IPs for this network on the parent network (if not set yet, will be nil). - routerExtPortIPv4 := net.ParseIP(n.config[ovnVolatileParentIPv4]) - routerExtPortIPv6 := net.ParseIP(n.config[ovnVolatileParentIPv6]) + // Parse existing allocated IPs for this network on the uplink network (if not set yet, will be nil). + routerExtPortIPv4 := net.ParseIP(n.config[ovnVolatileUplinkIPv4]) + routerExtPortIPv6 := net.ParseIP(n.config[ovnVolatileUplinkIPv6]) // Decide whether we need to allocate new IP(s) and go to the expense of retrieving all allocated IPs. - if (parentIPv4Net != nil && routerExtPortIPv4 == nil) || (parentIPv6Net != nil && routerExtPortIPv6 == nil) { + if (uplinkIPv4Net != nil && routerExtPortIPv4 == nil) || (uplinkIPv6Net != nil && routerExtPortIPv6 == nil) { err := n.state.Cluster.Transaction(func(tx *db.ClusterTx) error { - allAllocatedIPv4, allAllocatedIPv6, err := n.parentAllAllocatedIPs(tx, parentNet.Name()) + allAllocatedIPv4, allAllocatedIPv6, err := n.uplinkAllAllocatedIPs(tx, uplinkNet.Name()) if err != nil { - return errors.Wrapf(err, "Failed to get all allocated IPs for parent") + return errors.Wrapf(err, "Failed to get all allocated IPs for uplink") } - if parentIPv4Net != nil && routerExtPortIPv4 == nil { - if parentNetConf["ipv4.ovn.ranges"] == "" { - return fmt.Errorf(`Missing required "ipv4.ovn.ranges" config key on parent network`) + if uplinkIPv4Net != nil && routerExtPortIPv4 == nil { + if uplinkNetConf["ipv4.ovn.ranges"] == "" { + return fmt.Errorf(`Missing required "ipv4.ovn.ranges" config key on uplink network`) } - ipRanges, err := parseIPRanges(parentNetConf["ipv4.ovn.ranges"], parentNet.DHCPv4Subnet()) + ipRanges, err := parseIPRanges(uplinkNetConf["ipv4.ovn.ranges"], uplinkNet.DHCPv4Subnet()) if err != nil { - return errors.Wrapf(err, "Failed to parse parent IPv4 OVN ranges") + return errors.Wrapf(err, "Failed to parse uplink IPv4 OVN ranges") } - routerExtPortIPv4, err = n.parentAllocateIP(ipRanges, allAllocatedIPv4) + routerExtPortIPv4, err = n.uplinkAllocateIP(ipRanges, allAllocatedIPv4) if err != nil { - return errors.Wrapf(err, "Failed to allocate parent IPv4 address") + return errors.Wrapf(err, "Failed to allocate uplink IPv4 address") } - n.config[ovnVolatileParentIPv4] = routerExtPortIPv4.String() + n.config[ovnVolatileUplinkIPv4] = routerExtPortIPv4.String() } - if parentIPv6Net != nil && routerExtPortIPv6 == nil { - // If IPv6 OVN ranges are specified by the parent, allocate from them. - if parentNetConf["ipv6.ovn.ranges"] != "" { - ipRanges, err := parseIPRanges(parentNetConf["ipv6.ovn.ranges"], parentNet.DHCPv6Subnet()) + if uplinkIPv6Net != nil && routerExtPortIPv6 == nil { + // If IPv6 OVN ranges are specified by the uplink, allocate from them. + if uplinkNetConf["ipv6.ovn.ranges"] != "" { + ipRanges, err := parseIPRanges(uplinkNetConf["ipv6.ovn.ranges"], uplinkNet.DHCPv6Subnet()) if err != nil { - return errors.Wrapf(err, "Failed to parse parent IPv6 OVN ranges") + return errors.Wrapf(err, "Failed to parse uplink IPv6 OVN ranges") } - routerExtPortIPv6, err = n.parentAllocateIP(ipRanges, allAllocatedIPv6) + routerExtPortIPv6, err = n.uplinkAllocateIP(ipRanges, allAllocatedIPv6) if err != nil { - return errors.Wrapf(err, "Failed to allocate parent IPv6 address") + return errors.Wrapf(err, "Failed to allocate uplink IPv6 address") } } else { // Otherwise use EUI64 derived from MAC address. - routerExtPortIPv6, err = eui64.ParseMAC(parentIPv6Net.IP, routerMAC) + routerExtPortIPv6, err = eui64.ParseMAC(uplinkIPv6Net.IP, routerMAC) if err != nil { return err } } - n.config[ovnVolatileParentIPv6] = routerExtPortIPv6.String() + n.config[ovnVolatileUplinkIPv6] = routerExtPortIPv6.String() } err = tx.UpdateNetwork(n.id, n.description, n.config) if err != nil { - return errors.Wrapf(err, "Failed saving allocated parent network IPs") + return errors.Wrapf(err, "Failed saving allocated uplink network IPs") } return nil @@ -524,17 +524,17 @@ func (n *ovn) allocateParentPortIPs(parentNet Network, routerMAC net.HardwareAdd } // Configure variables needed to configure OVN router. - if parentIPv4Net != nil && routerExtPortIPv4 != nil { + if uplinkIPv4Net != nil && routerExtPortIPv4 != nil { routerExtPortIPv4Net := &net.IPNet{ - Mask: parentIPv4Net.Mask, + Mask: uplinkIPv4Net.Mask, IP: routerExtPortIPv4, } v.routerExtPortIPv4Net = routerExtPortIPv4Net.String() } - if parentIPv6Net != nil { + if uplinkIPv6Net != nil { routerExtPortIPv6Net := &net.IPNet{ - Mask: parentIPv6Net.Mask, + Mask: uplinkIPv6Net.Mask, IP: routerExtPortIPv6, } v.routerExtPortIPv6Net = routerExtPortIPv6Net.String() @@ -543,8 +543,8 @@ func (n *ovn) allocateParentPortIPs(parentNet Network, routerMAC net.HardwareAdd return v, nil } -// parentAllAllocatedIPs gets a list of all IPv4 and IPv6 addresses allocated to OVN networks connected to parent. -func (n *ovn) parentAllAllocatedIPs(tx *db.ClusterTx, parentNetName string) ([]net.IP, []net.IP, error) { +// uplinkAllAllocatedIPs gets a list of all IPv4 and IPv6 addresses allocated to OVN networks connected to uplink. +func (n *ovn) uplinkAllAllocatedIPs(tx *db.ClusterTx, uplinkNetName string) ([]net.IP, []net.IP, error) { // Get all managed networks across all projects. projectNetworks, err := tx.GetNonPendingNetworks() if err != nil { @@ -556,11 +556,11 @@ func (n *ovn) parentAllAllocatedIPs(tx *db.ClusterTx, parentNetName string) ([]n for _, networks := range projectNetworks { for _, netInfo := range networks { - if netInfo.Type != "ovn" || netInfo.Config["network"] != parentNetName { + if netInfo.Type != "ovn" || netInfo.Config["network"] != uplinkNetName { continue } - for _, k := range []string{ovnVolatileParentIPv4, ovnVolatileParentIPv6} { + for _, k := range []string{ovnVolatileUplinkIPv4, ovnVolatileUplinkIPv6} { if netInfo.Config[k] != "" { ip := net.ParseIP(netInfo.Config[k]) if ip != nil { @@ -578,8 +578,8 @@ func (n *ovn) parentAllAllocatedIPs(tx *db.ClusterTx, parentNetName string) ([]n return v4IPs, v6IPs, nil } -// parentAllocateIP allocates a free IP from one of the IP ranges. -func (n *ovn) parentAllocateIP(ipRanges []*shared.IPRange, allAllocated []net.IP) (net.IP, error) { +// uplinkAllocateIP allocates a free IP from one of the IP ranges. +func (n *ovn) uplinkAllocateIP(ipRanges []*shared.IPRange, allAllocated []net.IP) (net.IP, error) { for _, ipRange := range ipRanges { inc := big.NewInt(1) @@ -629,73 +629,73 @@ func (n *ovn) parentAllocateIP(ipRanges []*shared.IPRange, allAllocated []net.IP return nil, fmt.Errorf("No free IPs available") } -// startParentPort performs any network start up logic needed to connect the parent uplink connection to OVN. -func (n *ovn) startParentPort() error { - // Parent network must be in default project. - parentNet, err := LoadByName(n.state, project.Default, n.config["network"]) +// startUplinkPort performs any network start up logic needed to connect the uplink connection to OVN. +func (n *ovn) startUplinkPort() error { + // Uplink network must be in default project. + uplinkNet, err := LoadByName(n.state, project.Default, n.config["network"]) if err != nil { - return errors.Wrapf(err, "Failed loading parent network") + return errors.Wrapf(err, "Failed loading uplink network") } - // Lock parent network so that if multiple OVN networks are trying to connect to the same parent we don't + // Lock uplink network so that if multiple OVN networks are trying to connect to the same uplink we don't // race each other setting up the connection. - unlock := locking.Lock(n.parentOperationLockName(parentNet)) + unlock := locking.Lock(n.uplinkOperationLockName(uplinkNet)) defer unlock() - switch parentNet.Type() { + switch uplinkNet.Type() { case "bridge": - return n.startParentPortBridge(parentNet) + return n.startUplinkPortBridge(uplinkNet) case "physical": - return n.startParentPortPhysical(parentNet) + return n.startUplinkPortPhysical(uplinkNet) } - return fmt.Errorf("Failed starting parent port, network type %q unsupported as OVN parent", parentNet.Type()) + return fmt.Errorf("Failed starting uplink port, network type %q unsupported as OVN uplink", uplinkNet.Type()) } -// parentOperationLockName returns the lock name to use for operations on the parent network. -func (n *ovn) parentOperationLockName(parentNet Network) string { - return fmt.Sprintf("network.ovn.%s", parentNet.Name()) +// uplinkOperationLockName returns the lock name to use for operations on the uplink network. +func (n *ovn) uplinkOperationLockName(uplinkNet Network) string { + return fmt.Sprintf("network.ovn.%s", uplinkNet.Name()) } -// parentPortBridgeVars returns the parent port bridge variables needed for port start/stop. -func (n *ovn) parentPortBridgeVars(parentNet Network) *ovnParentPortBridgeVars { - ovsBridge := fmt.Sprintf("lxdovn%d", parentNet.ID()) +// uplinkPortBridgeVars returns the uplink port bridge variables needed for port start/stop. +func (n *ovn) uplinkPortBridgeVars(uplinkNet Network) *ovnUplinkPortBridgeVars { + ovsBridge := fmt.Sprintf("lxdovn%d", uplinkNet.ID()) - return &ovnParentPortBridgeVars{ + return &ovnUplinkPortBridgeVars{ ovsBridge: ovsBridge, - parentEnd: fmt.Sprintf("%sa", ovsBridge), + uplinkEnd: fmt.Sprintf("%sa", ovsBridge), ovsEnd: fmt.Sprintf("%sb", ovsBridge), } } -// startParentPortBridge creates veth pair (if doesn't exist), creates OVS bridge (if doesn't exist) and -// connects veth pair to parent bridge and OVS bridge. -func (n *ovn) startParentPortBridge(parentNet Network) error { - vars := n.parentPortBridgeVars(parentNet) +// startUplinkPortBridge creates veth pair (if doesn't exist), creates OVS bridge (if doesn't exist) and +// connects veth pair to uplink bridge and OVS bridge. +func (n *ovn) startUplinkPortBridge(uplinkNet Network) error { + vars := n.uplinkPortBridgeVars(uplinkNet) // Do this after gaining lock so that on failure we revert before release locking. revert := revert.New() defer revert.Fail() // Create veth pair if needed. - if !InterfaceExists(vars.parentEnd) && !InterfaceExists(vars.ovsEnd) { - _, err := shared.RunCommand("ip", "link", "add", "dev", vars.parentEnd, "type", "veth", "peer", "name", vars.ovsEnd) + if !InterfaceExists(vars.uplinkEnd) && !InterfaceExists(vars.ovsEnd) { + _, err := shared.RunCommand("ip", "link", "add", "dev", vars.uplinkEnd, "type", "veth", "peer", "name", vars.ovsEnd) if err != nil { - return errors.Wrapf(err, "Failed to create the uplink veth interfaces %q and %q", vars.parentEnd, vars.ovsEnd) + return errors.Wrapf(err, "Failed to create the uplink veth interfaces %q and %q", vars.uplinkEnd, vars.ovsEnd) } - revert.Add(func() { shared.RunCommand("ip", "link", "delete", vars.parentEnd) }) + revert.Add(func() { shared.RunCommand("ip", "link", "delete", vars.uplinkEnd) }) } // Ensure that the veth interfaces inherit the uplink bridge's MTU (which the OVS bridge also inherits). - parentNetConfig := parentNet.Config() - if parentNetConfig["bridge.mtu"] != "" { - err := InterfaceSetMTU(vars.parentEnd, parentNetConfig["bridge.mtu"]) + uplinkNetConfig := uplinkNet.Config() + if uplinkNetConfig["bridge.mtu"] != "" { + err := InterfaceSetMTU(vars.uplinkEnd, uplinkNetConfig["bridge.mtu"]) if err != nil { return err } - err = InterfaceSetMTU(vars.ovsEnd, parentNetConfig["bridge.mtu"]) + err = InterfaceSetMTU(vars.ovsEnd, uplinkNetConfig["bridge.mtu"]) if err != nil { return err } @@ -703,55 +703,55 @@ func (n *ovn) startParentPortBridge(parentNet Network) error { // Ensure correct sysctls are set on uplink veth interfaces to avoid getting IPv6 link-local addresses. err := util.SysctlSet( - fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6", vars.parentEnd), "1", + fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6", vars.uplinkEnd), "1", fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6", vars.ovsEnd), "1", - fmt.Sprintf("net/ipv6/conf/%s/forwarding", vars.parentEnd), "0", + fmt.Sprintf("net/ipv6/conf/%s/forwarding", vars.uplinkEnd), "0", fmt.Sprintf("net/ipv6/conf/%s/forwarding", vars.ovsEnd), "0", ) if err != nil { - return errors.Wrapf(err, "Failed to configure uplink veth interfaces %q and %q", vars.parentEnd, vars.ovsEnd) + return errors.Wrapf(err, "Failed to configure uplink veth interfaces %q and %q", vars.uplinkEnd, vars.ovsEnd) } - // Connect parent end of veth pair to parent bridge and bring up. - _, err = shared.RunCommand("ip", "link", "set", "master", parentNet.Name(), "dev", vars.parentEnd, "up") + // Connect uplink end of veth pair to uplink bridge and bring up. + _, err = shared.RunCommand("ip", "link", "set", "master", uplinkNet.Name(), "dev", vars.uplinkEnd, "up") if err != nil { - return errors.Wrapf(err, "Failed to connect uplink veth interface %q to parent bridge %q", vars.parentEnd, parentNet.Name()) + return errors.Wrapf(err, "Failed to connect uplink veth interface %q to uplink bridge %q", vars.uplinkEnd, uplinkNet.Name()) } // Ensure uplink OVS end veth interface is up. _, err = shared.RunCommand("ip", "link", "set", "dev", vars.ovsEnd, "up") if err != nil { - return errors.Wrapf(err, "Failed to bring up parent veth interface %q", vars.ovsEnd) + return errors.Wrapf(err, "Failed to bring up uplink veth interface %q", vars.ovsEnd) } - // Create parent OVS bridge if needed. + // Create uplink OVS bridge if needed. ovs := openvswitch.NewOVS() err = ovs.BridgeAdd(vars.ovsBridge, true) if err != nil { - return errors.Wrapf(err, "Failed to create parent uplink OVS bridge %q", vars.ovsBridge) + return errors.Wrapf(err, "Failed to create uplink OVS bridge %q", vars.ovsBridge) } // Connect OVS end veth interface to OVS bridge. err = ovs.BridgePortAdd(vars.ovsBridge, vars.ovsEnd, true) if err != nil { - return errors.Wrapf(err, "Failed to connect uplink veth interface %q to parent OVS bridge %q", vars.ovsEnd, vars.ovsBridge) + return errors.Wrapf(err, "Failed to connect uplink veth interface %q to uplink OVS bridge %q", vars.ovsEnd, vars.ovsBridge) } // Associate OVS bridge to logical OVN provider. - err = ovs.OVNBridgeMappingAdd(vars.ovsBridge, parentNet.Name()) + err = ovs.OVNBridgeMappingAdd(vars.ovsBridge, uplinkNet.Name()) if err != nil { - return errors.Wrapf(err, "Failed to associate parent OVS bridge %q to OVN provider %q", vars.ovsBridge, parentNet.Name()) + return errors.Wrapf(err, "Failed to associate uplink OVS bridge %q to OVN provider %q", vars.ovsBridge, uplinkNet.Name()) } - routerExtPortIPv6 := net.ParseIP(n.config[ovnVolatileParentIPv6]) + routerExtPortIPv6 := net.ParseIP(n.config[ovnVolatileUplinkIPv6]) if routerExtPortIPv6 != nil { - // Now that the OVN router is connected to the uplink parent bridge, attempt to ping the OVN - // router's external IPv6 from the LXD host running the parent bridge in an attempt to trigger the - // OVN router to learn the parent uplink gateway's MAC address. This is to work around a bug in + // Now that the OVN router is connected to the uplink bridge, attempt to ping the OVN + // router's external IPv6 from the LXD host running the uplink bridge in an attempt to trigger the + // OVN router to learn the uplink gateway's MAC address. This is to work around a bug in // older versions of OVN that meant that the OVN router would not attempt to learn the external // uplink IPv6 gateway MAC address when using SNAT, meaning that external IPv6 connectivity // wouldn't work until the next router advertisement was sent (which could be several minutes). - // By pinging the OVN router's external IP this will trigger an NDP request from the parent bridge + // By pinging the OVN router's external IP this will trigger an NDP request from the uplink bridge // which will cause the OVN router to learn its MAC address. go func() { // Try several attempts as it can take a few seconds for the network to come up. @@ -774,19 +774,19 @@ func (n *ovn) startParentPortBridge(parentNet Network) error { return nil } -// startParentPortPhysical creates OVS bridge (if doesn't exist) and connects parent interface to the OVS bridge. -func (n *ovn) startParentPortPhysical(parentNet Network) error { - vars := n.parentPortBridgeVars(parentNet) +// startUplinkPortPhysical creates OVS bridge (if doesn't exist) and connects uplink interface to the OVS bridge. +func (n *ovn) startUplinkPortPhysical(uplinkNet Network) error { + vars := n.uplinkPortBridgeVars(uplinkNet) // Do this after gaining lock so that on failure we revert before release locking. revert := revert.New() defer revert.Fail() - parentConfig := parentNet.Config() - uplinkHostName := GetHostDevice(parentConfig["parent"], parentConfig["vlan"]) + uplinkConfig := uplinkNet.Config() + uplinkHostName := GetHostDevice(uplinkConfig["parent"], uplinkConfig["vlan"]) if !InterfaceExists(uplinkHostName) { - return fmt.Errorf("Uplink network %q is not started", parentNet.Name()) + return fmt.Errorf("Uplink network %q is not started", uplinkNet.Name()) } // Ensure correct sysctls are set on uplink interface to avoid getting IPv6 link-local addresses. @@ -798,29 +798,29 @@ func (n *ovn) startParentPortPhysical(parentNet Network) error { return errors.Wrapf(err, "Failed to configure uplink interface %q", uplinkHostName) } - // Create parent OVS bridge if needed. + // Create uplink OVS bridge if needed. ovs := openvswitch.NewOVS() err = ovs.BridgeAdd(vars.ovsBridge, true) if err != nil { - return errors.Wrapf(err, "Failed to create parent uplink OVS bridge %q", vars.ovsBridge) + return errors.Wrapf(err, "Failed to create uplink OVS bridge %q", vars.ovsBridge) } // Connect OVS end veth interface to OVS bridge. err = ovs.BridgePortAdd(vars.ovsBridge, uplinkHostName, true) if err != nil { - return errors.Wrapf(err, "Failed to connect uplink interface %q to parent OVS bridge %q", uplinkHostName, vars.ovsBridge) + return errors.Wrapf(err, "Failed to connect uplink interface %q to uplink OVS bridge %q", uplinkHostName, vars.ovsBridge) } // Associate OVS bridge to logical OVN provider. - err = ovs.OVNBridgeMappingAdd(vars.ovsBridge, parentNet.Name()) + err = ovs.OVNBridgeMappingAdd(vars.ovsBridge, uplinkNet.Name()) if err != nil { - return errors.Wrapf(err, "Failed to associate parent OVS bridge %q to OVN provider %q", vars.ovsBridge, parentNet.Name()) + return errors.Wrapf(err, "Failed to associate uplink OVS bridge %q to OVN provider %q", vars.ovsBridge, uplinkNet.Name()) } // Bring uplink interface up. _, err = shared.RunCommand("ip", "link", "set", uplinkHostName, "up") if err != nil { - return errors.Wrapf(err, "Failed to bring up parent interface %q", uplinkHostName) + return errors.Wrapf(err, "Failed to bring up uplink interface %q", uplinkHostName) } revert.Success() @@ -847,7 +847,7 @@ func (n *ovn) checkUplinkUse() (bool, error) { continue // Ignore our own DB record or non OVN networks. } - // Check if another network is using our parent. + // Check if another network is using our uplink. if network.Config["network"] == n.config["network"] { return true, nil } @@ -857,37 +857,37 @@ func (n *ovn) checkUplinkUse() (bool, error) { return false, nil } -// deleteParentPort deletes the parent uplink connection. -func (n *ovn) deleteParentPort() error { - // Parent network must be in default project. +// deleteUplinkPort deletes the uplink connection. +func (n *ovn) deleteUplinkPort() error { + // Uplink network must be in default project. if n.config["network"] != "" { - parentNet, err := LoadByName(n.state, project.Default, n.config["network"]) + uplinkNet, err := LoadByName(n.state, project.Default, n.config["network"]) if err != nil { - return errors.Wrapf(err, "Failed loading parent network") + return errors.Wrapf(err, "Failed loading uplink network") } - // Lock parent network so we don't race each other networks using the OVS uplink bridge. - unlock := locking.Lock(n.parentOperationLockName(parentNet)) + // Lock uplink network so we don't race each other networks using the OVS uplink bridge. + unlock := locking.Lock(n.uplinkOperationLockName(uplinkNet)) defer unlock() - switch parentNet.Type() { + switch uplinkNet.Type() { case "bridge": - return n.deleteParentPortBridge(parentNet) + return n.deleteUplinkPortBridge(uplinkNet) case "physical": - return n.deleteParentPortPhysical(parentNet) + return n.deleteUplinkPortPhysical(uplinkNet) } - return fmt.Errorf("Failed deleting parent port, network type %q unsupported as OVN parent", parentNet.Type()) + return fmt.Errorf("Failed deleting uplink port, network type %q unsupported as OVN uplink", uplinkNet.Type()) } return nil } -// deleteParentPortBridge deletes parent uplink OVS bridge, OVN bridge mappings and veth interfaces if not in use. -func (n *ovn) deleteParentPortBridge(parentNet Network) error { +// deleteUplinkPortBridge deletes uplink OVS bridge, OVN bridge mappings and veth interfaces if not in use. +func (n *ovn) deleteUplinkPortBridge(uplinkNet Network) error { // Check OVS uplink bridge exists, if it does, check whether the uplink network is in use. removeVeths := false - vars := n.parentPortBridgeVars(parentNet) + vars := n.uplinkPortBridgeVars(uplinkNet) if InterfaceExists(vars.ovsBridge) { uplinkUsed, err := n.checkUplinkUse() if err != nil { @@ -899,7 +899,7 @@ func (n *ovn) deleteParentPortBridge(parentNet Network) error { removeVeths = true ovs := openvswitch.NewOVS() - err = ovs.OVNBridgeMappingDelete(vars.ovsBridge, parentNet.Name()) + err = ovs.OVNBridgeMappingDelete(vars.ovsBridge, uplinkNet.Name()) if err != nil { return err } @@ -915,10 +915,10 @@ func (n *ovn) deleteParentPortBridge(parentNet Network) error { // Remove the veth interfaces if they exist. if removeVeths { - if InterfaceExists(vars.parentEnd) { - _, err := shared.RunCommand("ip", "link", "delete", "dev", vars.parentEnd) + if InterfaceExists(vars.uplinkEnd) { + _, err := shared.RunCommand("ip", "link", "delete", "dev", vars.uplinkEnd) if err != nil { - return errors.Wrapf(err, "Failed to delete the uplink veth interface %q", vars.parentEnd) + return errors.Wrapf(err, "Failed to delete the uplink veth interface %q", vars.uplinkEnd) } } @@ -933,11 +933,11 @@ func (n *ovn) deleteParentPortBridge(parentNet Network) error { return nil } -// deleteParentPortPhysical deletes parent uplink OVS bridge and OVN bridge mappings if not in use. -func (n *ovn) deleteParentPortPhysical(parentNet Network) error { +// deleteUplinkPortPhysical deletes uplink OVS bridge and OVN bridge mappings if not in use. +func (n *ovn) deleteUplinkPortPhysical(uplinkNet Network) error { // Check OVS uplink bridge exists, if it does, check whether the uplink network is in use. releaseIF := false - vars := n.parentPortBridgeVars(parentNet) + vars := n.uplinkPortBridgeVars(uplinkNet) if InterfaceExists(vars.ovsBridge) { uplinkUsed, err := n.checkUplinkUse() if err != nil { @@ -949,7 +949,7 @@ func (n *ovn) deleteParentPortPhysical(parentNet Network) error { releaseIF = true ovs := openvswitch.NewOVS() - err = ovs.OVNBridgeMappingDelete(vars.ovsBridge, parentNet.Name()) + err = ovs.OVNBridgeMappingDelete(vars.ovsBridge, uplinkNet.Name()) if err != nil { return err } @@ -965,12 +965,12 @@ func (n *ovn) deleteParentPortPhysical(parentNet Network) error { // Bring down uplink interface if exists. if releaseIF { - parentConfig := parentNet.Config() - parentDev := GetHostDevice(parentConfig["parent"], parentConfig["vlan"]) - if InterfaceExists(parentDev) { - _, err := shared.RunCommand("ip", "link", "set", parentDev, "down") + uplinkConfig := uplinkNet.Config() + uplinkDev := GetHostDevice(uplinkConfig["parent"], uplinkConfig["vlan"]) + if InterfaceExists(uplinkDev) { + _, err := shared.RunCommand("ip", "link", "set", uplinkDev, "down") if err != nil { - return errors.Wrapf(err, "Failed to bring down uplink interface %q", parentDev) + return errors.Wrapf(err, "Failed to bring down uplink interface %q", uplinkDev) } } } @@ -1171,24 +1171,24 @@ func (n *ovn) setup(update bool) error { return err } - // Setup parent port (do this first to check parent is suitable). - parent, err := n.setupParentPort(routerMAC) + // Setup uplink port (do this first to check uplink is suitable). + uplinkNet, err := n.setupUplinkPort(routerMAC) if err != nil { return err } // Parse router IP config. - if parent.routerExtPortIPv4Net != "" { - routerExtPortIPv4, routerExtPortIPv4Net, err = net.ParseCIDR(parent.routerExtPortIPv4Net) + if uplinkNet.routerExtPortIPv4Net != "" { + routerExtPortIPv4, routerExtPortIPv4Net, err = net.ParseCIDR(uplinkNet.routerExtPortIPv4Net) if err != nil { - return errors.Wrapf(err, "Failed parsing router's external parent port IPv4 Net") + return errors.Wrapf(err, "Failed parsing router's external uplink port IPv4 Net") } } - if parent.routerExtPortIPv6Net != "" { - routerExtPortIPv6, routerExtPortIPv6Net, err = net.ParseCIDR(parent.routerExtPortIPv6Net) + if uplinkNet.routerExtPortIPv6Net != "" { + routerExtPortIPv6, routerExtPortIPv6Net, err = net.ParseCIDR(uplinkNet.routerExtPortIPv6Net) if err != nil { - return errors.Wrapf(err, "Failed parsing router's external parent port IPv6 Net") + return errors.Wrapf(err, "Failed parsing router's external uplink port IPv6 Net") } } @@ -1228,15 +1228,15 @@ func (n *ovn) setup(update bool) error { // Configure logical router. // Add default routes. - if parent.routerExtGwIPv4 != nil { - err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}, parent.routerExtGwIPv4) + if uplinkNet.routerExtGwIPv4 != nil { + err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}, uplinkNet.routerExtGwIPv4) if err != nil { return errors.Wrapf(err, "Failed adding IPv4 default route") } } - if parent.routerExtGwIPv6 != nil { - err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}, parent.routerExtGwIPv6) + if uplinkNet.routerExtGwIPv6 != nil { + err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}, uplinkNet.routerExtGwIPv6) if err != nil { return errors.Wrapf(err, "Failed adding IPv6 default route") } @@ -1317,7 +1317,7 @@ func (n *ovn) setup(update bool) error { } revert.Add(func() { client.LogicalSwitchPortDelete(n.getExtSwitchProviderPortName()) }) - err = client.LogicalSwitchPortLinkProviderNetwork(n.getExtSwitchProviderPortName(), parent.extSwitchProviderName) + err = client.LogicalSwitchPortLinkProviderNetwork(n.getExtSwitchProviderPortName(), uplinkNet.extSwitchProviderName) if err != nil { return errors.Wrapf(err, "Failed linking external switch provider port to external provider network") } @@ -1367,7 +1367,7 @@ func (n *ovn) setup(update bool) error { ServerID: routerIntPortIPv4, ServerMAC: routerMAC, Router: routerIntPortIPv4, - RecursiveDNSServer: parent.dnsIPv4, + RecursiveDNSServer: uplinkNet.dnsIPv4, DomainName: n.getDomainName(), LeaseTime: time.Duration(time.Hour * 1), MTU: bridgeMTU, @@ -1379,7 +1379,7 @@ func (n *ovn) setup(update bool) error { // Create DHCPv6 options for internal switch. err = client.LogicalSwitchDHCPv6OptionsSet(n.getIntSwitchName(), dhcpv6UUID, routerIntPortIPv6Net, &openvswitch.OVNDHCPv6Opts{ ServerID: routerMAC, - RecursiveDNSServer: parent.dnsIPv6, + RecursiveDNSServer: uplinkNet.dnsIPv6, DNSSearchList: n.getDNSSearchList(), }) if err != nil { @@ -1417,8 +1417,8 @@ func (n *ovn) setup(update bool) error { } var recursiveDNSServer net.IP - if len(parent.dnsIPv6) > 0 { - recursiveDNSServer = parent.dnsIPv6[0] // OVN only supports 1 RA DNS server. + if len(uplinkNet.dnsIPv6) > 0 { + recursiveDNSServer = uplinkNet.dnsIPv6[0] // OVN only supports 1 RA DNS server. } err = client.LogicalRouterPortSetIPv6Advertisements(n.getRouterIntPortName(), &openvswitch.OVNIPv6RAOpts{ @@ -1577,7 +1577,7 @@ func (n *ovn) Rename(newName string) error { return nil } -// Start starts adds the local OVS chassis ID to the OVN chass group and starts the local OVS parent uplink port. +// Start starts adds the local OVS chassis ID to the OVN chass group and starts the local OVS uplink port. func (n *ovn) Start() error { n.logger.Debug("Start") @@ -1591,7 +1591,7 @@ func (n *ovn) Start() error { return err } - err = n.startParentPort() + err = n.startUplinkPort() if err != nil { return err } @@ -1599,7 +1599,7 @@ func (n *ovn) Start() error { return nil } -// Stop deletes the local OVS parent uplink port (if unused) and deletes the local OVS chassis ID from the +// Stop deletes the local OVS uplink port (if unused) and deletes the local OVS chassis ID from the // OVN chass group func (n *ovn) Stop() error { n.logger.Debug("Stop") @@ -1610,8 +1610,8 @@ func (n *ovn) Stop() error { return err } - // Delete local parent uplink port if not used by other OVN networks. - err = n.deleteParentPort() + // Delete local uplink port if not used by other OVN networks. + err = n.deleteUplinkPort() if err != nil { return err } @@ -1663,8 +1663,8 @@ func (n *ovn) Update(newNetwork api.NetworkPut, targetNode string, clientType cl } // Remove volatile keys associated with old network in new config. - delete(newNetwork.Config, ovnVolatileParentIPv4) - delete(newNetwork.Config, ovnVolatileParentIPv6) + delete(newNetwork.Config, ovnVolatileUplinkIPv4) + delete(newNetwork.Config, ovnVolatileUplinkIPv6) } // Apply changes to all nodes and databse. From 406d4fc37c9b00a0065e237fc74e0122acea92a6 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 09:36:01 +0100 Subject: [PATCH 8/8] lxd/network/driver/common: Ban : char from network names in ValidateName() This prevents network names from conflicting with vlan alias interface names and when used as a prefix for `restricted.networks.subnets` in projects. Signed-off-by: Thomas Parrott --- lxd/network/driver_common.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lxd/network/driver_common.go b/lxd/network/driver_common.go index 3c422c5a1a..23cff9a58c 100644 --- a/lxd/network/driver_common.go +++ b/lxd/network/driver_common.go @@ -101,7 +101,16 @@ func (n *common) validate(config map[string]string, driverRules map[string]func( // ValidateName validates network name. func (n *common) ValidateName(name string) error { - return validate.IsURLSegmentSafe(name) + err := validate.IsURLSegmentSafe(name) + if err != nil { + return err + } + + if strings.Contains(name, ":") { + return fmt.Errorf("Cannot contain %q", ":") + } + + return nil } // ID returns the network ID. From lxc-bot at linuxcontainers.org Tue Oct 13 15:50:19 2020 From: lxc-bot at linuxcontainers.org (Drachenfels-GmbH on Github) Date: Tue, 13 Oct 2020 08:50:19 -0700 (PDT) Subject: [lxc-devel] [lxc/master] seccomp: Check if syscall is supported on compat architecture. Message-ID: <5f85ccbb.1c69fb81.4a3de.4c3fSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 4699 bytes Desc: not available URL: -------------- next part -------------- From fbec5f832bf5871e97619f56a8dd511d379c9d05 Mon Sep 17 00:00:00 2001 From: Ruben Jenster Date: Tue, 13 Oct 2020 16:51:55 +0200 Subject: [PATCH] seccomp: Check if syscall is supported on compat architecture. Signed-off-by: Ruben Jenster --- src/lxc/seccomp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c index 06296f5d94..61b9954a86 100644 --- a/src/lxc/seccomp.c +++ b/src/lxc/seccomp.c @@ -531,6 +531,11 @@ static bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx, return true; } + if (arch != SCMP_ARCH_NATIVE && seccomp_syscall_resolve_name_arch(arch, line) < 0) { + INFO("The syscall \"%s\" nr:%d is not supported on compat arch:%d", line, nr, arch); + return true; + } + memset(&arg_cmp, 0, sizeof(arg_cmp)); for (i = 0; i < rule->args_num; i++) { INFO("arg_cmp[%d]: SCMP_CMP(%u, %llu, %llu, %llu)", i, From noreply at github.com Tue Oct 13 20:12:51 2020 From: noreply at github.com (Christian Brauner) Date: Tue, 13 Oct 2020 13:12:51 -0700 Subject: [lxc-devel] [lxc/lxc] fbec5f: seccomp: Check if syscall is supported on compat a... Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: fbec5f832bf5871e97619f56a8dd511d379c9d05 https://github.com/lxc/lxc/commit/fbec5f832bf5871e97619f56a8dd511d379c9d05 Author: Ruben Jenster Date: 2020-10-13 (Tue, 13 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- seccomp: Check if syscall is supported on compat architecture. Signed-off-by: Ruben Jenster Commit: 186ff2beaffabd4dc75a5cbe9fa936a67037d155 https://github.com/lxc/lxc/commit/186ff2beaffabd4dc75a5cbe9fa936a67037d155 Author: Christian Brauner Date: 2020-10-13 (Tue, 13 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- Merge pull request #3548 from Drachenfels-GmbH/master seccomp: Check if syscall is supported on compat architecture. Compare: https://github.com/lxc/lxc/compare/11d123becb02...186ff2beaffa From builds at travis-ci.org Tue Oct 13 20:27:26 2020 From: builds at travis-ci.org (Travis CI) Date: Tue, 13 Oct 2020 20:27:26 +0000 Subject: [lxc-devel] Passed: lxc/lxc#7891 (master - 186ff2b) In-Reply-To: Message-ID: <5f860dadae7db_13fb56e61cafc9922@travis-tasks-84c9f794d6-6b9p2.mail> Build Update for lxc/lxc ------------------------------------- Build: #7891 Status: Passed Duration: 13 mins and 47 secs Commit: 186ff2b (master) Author: Christian Brauner Message: Merge pull request #3548 from Drachenfels-GmbH/master seccomp: Check if syscall is supported on compat architecture. View the changeset: https://github.com/lxc/lxc/compare/11d123becb02...186ff2beaffa View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/735519769?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lxc-bot at linuxcontainers.org Wed Oct 14 01:44:43 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Tue, 13 Oct 2020 18:44:43 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/driver/qemu: Add spice usb ports Message-ID: <5f86580b.1c69fb81.cb5db.5dc4SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From 8f9c48459da0b8256c1baca34f0add536ba1e71a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 13 Oct 2020 21:44:24 -0400 Subject: [PATCH] lxd/driver/qemu: Add spice usb ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/instance/drivers/driver_qemu_templates.go | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lxd/instance/drivers/driver_qemu_templates.go b/lxd/instance/drivers/driver_qemu_templates.go index b77364d877..29d4aa6b40 100644 --- a/lxd/instance/drivers/driver_qemu_templates.go +++ b/lxd/instance/drivers/driver_qemu_templates.go @@ -507,6 +507,30 @@ addr = "{{.devAddr}}" {{if .multifunction -}} multifunction = "on" {{- end }} + +[chardev "qemu_spice-usb-chardev1"] + backend = "spicevmc" + name = "usbredir" + +[chardev "qemu_spice-usb-chardev2"] + backend = "spicevmc" + name = "usbredir" + +[chardev "qemu_spice-usb-chardev3"] + backend = "spicevmc" + name = "usbredir" + +[device "qemu_spice-usb1"] + driver = "usb-redir" + chardev = "qemu_spice-usb-chardev1" + +[device "qemu_spice-usb2"] + driver = "usb-redir" + chardev = "qemu_spice-usb-chardev2" + +[device "qemu_spice-usb3"] + driver = "usb-redir" + chardev = "qemu_spice-usb-chardev3" `)) var qemuUSBDev = template.Must(template.New("qemuUSBDev").Parse(` From lxc-bot at linuxcontainers.org Wed Oct 14 12:21:39 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Wed, 14 Oct 2020 05:21:39 -0700 (PDT) Subject: [lxc-devel] [distrobuilder/master] Support building Windows VM images Message-ID: <5f86ed53.1c69fb81.b1243.e299SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 310 bytes Desc: not available URL: -------------- next part -------------- From 5994aadde58bd4bc7a36f6ad72e811f03fd65bfb Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Wed, 14 Oct 2020 13:24:47 +0200 Subject: [PATCH 01/10] sources: Add Windows source Signed-off-by: Thomas Hipp --- sources/source.go | 2 ++ sources/windows.go | 86 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 sources/windows.go diff --git a/sources/source.go b/sources/source.go index fce32c5..cf33b34 100644 --- a/sources/source.go +++ b/sources/source.go @@ -44,6 +44,8 @@ func Get(name string) Downloader { return NewVoidLinuxHTTP() case "funtoo-http": return NewFuntooHTTP() + case "windows": + return NewWindows() } return nil diff --git a/sources/windows.go b/sources/windows.go new file mode 100644 index 0000000..b2c619d --- /dev/null +++ b/sources/windows.go @@ -0,0 +1,86 @@ +package sources + +import ( + "bytes" + "fmt" + "net/url" + "os" + "path/filepath" + "strconv" + "strings" + + lxd "github.com/lxc/lxd/shared" + "github.com/pkg/errors" + + "github.com/lxc/distrobuilder/shared" +) + +// Windows represents the Windows OS +type Windows struct{} + +// NewWindows creates a new Windows instance. +func NewWindows() *Windows { + return &Windows{} +} + +// Run unpacks a Windows VHD file. +func (s *Windows) Run(definition shared.Definition, rootfsDir string) error { + // URL + u, err := url.Parse(definition.Source.URL) + if err != nil { + return errors.Wrap(err, "Failed to parse URL") + } + + if u.Scheme != "file" { + return fmt.Errorf("Scheme %q is not supported", u.Scheme) + } + + rawFilePath := fmt.Sprintf("%s.raw", u.Path) + + if !lxd.PathExists(rawFilePath) { + // Convert the vhd image to raw. + err = shared.RunCommand("qemu-img", "convert", "-O", "raw", u.Path, rawFilePath) + if err != nil { + return errors.Wrap(err, "Failed to convert image") + } + } + + // Figure out the offset + var buf bytes.Buffer + + err = lxd.RunCommandWithFds(nil, &buf, "fdisk", "-l", "-o", "Start", rawFilePath) + if err != nil { + return errors.Wrap(err, "Failed to list partitions") + } + + output := strings.Split(buf.String(), "\n") + offsetStr := strings.TrimSpace(output[len(output)-2]) + + offset, err := strconv.Atoi(offsetStr) + if err != nil { + return errors.Wrap(err, "Failed to read offset") + } + + roRootDir := filepath.Join(os.TempDir(), "distrobuilder", "rootfs.ro") + + err = os.MkdirAll(roRootDir, 0755) + if err != nil { + return errors.Wrap(err, "Failed to create directory") + } + defer os.RemoveAll(filepath.Join(os.TempDir(), "distrobuilder")) + + // Mount the partition read-only since we don't want to accidently modify it. + err = shared.RunCommand("mount", "-o", fmt.Sprintf("ro,loop,offset=%d", offset*512), + rawFilePath, roRootDir) + if err != nil { + return errors.Wrap(err, "Failed to mount partition read-only") + } + + // Copy the read-only rootfs to the real rootfs. + err = shared.RunCommand("rsync", "-qa", roRootDir+"/", rootfsDir) + if err != nil { + return errors.Wrap(err, "Failed to copy rootfs") + } + + return nil +} From 4e082ef4b1505926441647065c667acd629c7b0e Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Wed, 14 Oct 2020 13:25:36 +0200 Subject: [PATCH 02/10] shared/definition: Accept Windows as a source Signed-off-by: Thomas Hipp --- shared/definition.go | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/definition.go b/shared/definition.go index 5512cfe..1989795 100644 --- a/shared/definition.go +++ b/shared/definition.go @@ -336,6 +336,7 @@ func (d *Definition) Validate() error { "plamolinux-http", "voidlinux-http", "funtoo-http", + "windows", } if !shared.StringInSlice(strings.TrimSpace(d.Source.Downloader), validDownloaders) { return fmt.Errorf("source.downloader must be one of %v", validDownloaders) From 38feac9ce9829761840fd1004295b391bed7b8d7 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Wed, 14 Oct 2020 13:34:59 +0200 Subject: [PATCH 03/10] shared/definition: Ignore package manager when using Windows Signed-off-by: Thomas Hipp --- shared/definition.go | 80 +++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/shared/definition.go b/shared/definition.go index 1989795..dccdde7 100644 --- a/shared/definition.go +++ b/shared/definition.go @@ -342,51 +342,53 @@ func (d *Definition) Validate() error { return fmt.Errorf("source.downloader must be one of %v", validDownloaders) } - if d.Packages.Manager != "" { - validManagers := []string{ - "apk", - "apt", - "dnf", - "egoportage", - "opkg", - "pacman", - "portage", - "yum", - "equo", - "xbps", - "zypper", - "luet", - } - if !shared.StringInSlice(strings.TrimSpace(d.Packages.Manager), validManagers) { - return fmt.Errorf("packages.manager must be one of %v", validManagers) - } + if d.Source.Downloader != "windows" { + if d.Packages.Manager != "" { + validManagers := []string{ + "apk", + "apt", + "dnf", + "egoportage", + "opkg", + "pacman", + "portage", + "yum", + "equo", + "xbps", + "zypper", + "luet", + } + if !shared.StringInSlice(strings.TrimSpace(d.Packages.Manager), validManagers) { + return fmt.Errorf("packages.manager must be one of %v", validManagers) + } - if d.Packages.CustomManager != nil { - return fmt.Errorf("cannot have both packages.manager and packages.custom-manager set") - } - } else { - if d.Packages.CustomManager == nil { - return fmt.Errorf("packages.manager or packages.custom-manager needs to be set") - } + if d.Packages.CustomManager != nil { + return fmt.Errorf("cannot have both packages.manager and packages.custom-manager set") + } + } else { + if d.Packages.CustomManager == nil { + return fmt.Errorf("packages.manager or packages.custom-manager needs to be set") + } - if d.Packages.CustomManager.Clean.Command == "" { - return fmt.Errorf("packages.custom-manager requires a clean command") - } + if d.Packages.CustomManager.Clean.Command == "" { + return fmt.Errorf("packages.custom-manager requires a clean command") + } - if d.Packages.CustomManager.Install.Command == "" { - return fmt.Errorf("packages.custom-manager requires an install command") - } + if d.Packages.CustomManager.Install.Command == "" { + return fmt.Errorf("packages.custom-manager requires an install command") + } - if d.Packages.CustomManager.Remove.Command == "" { - return fmt.Errorf("packages.custom-manager requires a remove command") - } + if d.Packages.CustomManager.Remove.Command == "" { + return fmt.Errorf("packages.custom-manager requires a remove command") + } - if d.Packages.CustomManager.Refresh.Command == "" { - return fmt.Errorf("packages.custom-manager requires a refresh command") - } + if d.Packages.CustomManager.Refresh.Command == "" { + return fmt.Errorf("packages.custom-manager requires a refresh command") + } - if d.Packages.CustomManager.Update.Command == "" { - return fmt.Errorf("packages.custom-manager requires an update command") + if d.Packages.CustomManager.Update.Command == "" { + return fmt.Errorf("packages.custom-manager requires an update command") + } } } From 79691c4b107572f93982441f67e0c41c9f0fd9f0 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Wed, 14 Oct 2020 13:48:17 +0200 Subject: [PATCH 04/10] distrobuilder: Rename VM functions Signed-off-by: Thomas Hipp --- distrobuilder/main_lxd.go | 17 +++------ distrobuilder/vm.go | 80 +++++++++++++++++++-------------------- 2 files changed, 46 insertions(+), 51 deletions(-) diff --git a/distrobuilder/main_lxd.go b/distrobuilder/main_lxd.go index 2446ee6..2e1d67f 100644 --- a/distrobuilder/main_lxd.go +++ b/distrobuilder/main_lxd.go @@ -239,25 +239,20 @@ func (c *cmdLXD) run(cmd *cobra.Command, args []string, overlayDir string) error } defer vm.umountImage() - err = vm.createRootFS() + err = vm.createFilesystems() if err != nil { - return errors.Wrap(err, "Failed to create root filesystem") + return errors.Wrap(err, "Failed to create filesystems") } - err = vm.mountRootPartition() + err = vm.mountRootFilesystem() if err != nil { - return errors.Wrap(err, "failed to mount root partion") + return errors.Wrap(err, "Failed to mount root filesystem") } defer lxd.RunCommand("umount", "-R", vmDir) - err = vm.createUEFIFS() + err = vm.mountUEFIFilesystem() if err != nil { - return errors.Wrap(err, "Failed to create UEFI filesystem") - } - - err = vm.mountUEFIPartition() - if err != nil { - return errors.Wrap(err, "Failed to mount UEFI partition") + return errors.Wrap(err, "Failed to mount UEFI filesystem") } // We cannot use LXD's rsync package as that uses the --delete flag which diff --git a/distrobuilder/vm.go b/distrobuilder/vm.go index f6ba300..7939cfa 100644 --- a/distrobuilder/vm.go +++ b/distrobuilder/vm.go @@ -194,41 +194,6 @@ func (v *vm) umountImage() error { return nil } -func (v *vm) createRootFS() error { - if v.loopDevice == "" { - return fmt.Errorf("Disk image not mounted") - } - - switch v.rootFS { - case "btrfs": - err := shared.RunCommand("mkfs.btrfs", "-f", "-L", "rootfs", v.getRootfsDevFile()) - if err != nil { - return err - } - - // Create the root subvolume as well - err = shared.RunCommand("mount", v.getRootfsDevFile(), v.rootfsDir) - if err != nil { - return err - } - defer shared.RunCommand("umount", v.rootfsDir) - - return shared.RunCommand("btrfs", "subvolume", "create", fmt.Sprintf("%s/@", v.rootfsDir)) - case "ext4": - return shared.RunCommand("mkfs.ext4", "-F", "-b", "4096", "-i 8192", "-m", "0", "-L", "rootfs", "-E", "resize=536870912", v.getRootfsDevFile()) - } - - return nil -} - -func (v *vm) createUEFIFS() error { - if v.loopDevice == "" { - return fmt.Errorf("Disk image not mounted") - } - - return shared.RunCommand("mkfs.vfat", "-F", "32", "-n", "UEFI", v.getUEFIDevFile()) -} - func (v *vm) getRootfsPartitionUUID() (string, error) { if v.loopDevice == "" { return "", fmt.Errorf("Disk image not mounted") @@ -255,23 +220,58 @@ func (v *vm) getUEFIPartitionUUID() (string, error) { return strings.TrimSpace(stdout), nil } -func (v *vm) mountRootPartition() error { +func (v *vm) createFilesystems() error { if v.loopDevice == "" { return fmt.Errorf("Disk image not mounted") } + var err error + + // Create root filesystem switch v.rootFS { case "btrfs": - return shared.RunCommand("mount", v.getRootfsDevFile(), v.rootfsDir, "-o", "defaults,subvol=/@") + err := shared.RunCommand("mkfs.btrfs", "-f", "-L", "rootfs", v.getRootfsDevFile()) + if err != nil { + return err + } + + // Create the root subvolume as well + err = shared.RunCommand("mount", v.getRootfsDevFile(), v.rootfsDir) + if err != nil { + return err + } + defer shared.RunCommand("umount", v.rootfsDir) + + err = shared.RunCommand("btrfs", "subvolume", "create", fmt.Sprintf("%s/@", v.rootfsDir)) case "ext4": - return shared.RunCommand("mount", v.getRootfsDevFile(), v.rootfsDir) + err = shared.RunCommand("mkfs.ext4", "-F", "-b", "4096", "-i 8192", "-m", "0", "-L", "rootfs", "-E", "resize=536870912", v.getRootfsDevFile()) + } + if err != nil { + return err + } + + // Create UEFI filesystem + return shared.RunCommand("mkfs.vfat", "-F", "32", "-n", "UEFI", v.getUEFIDevFile()) +} +func (v *vm) mountRootFilesystem() error { + if v.loopDevice == "" { + return fmt.Errorf("Disk image not mounted") } - return nil + var err error + + switch v.rootFS { + case "btrfs": + err = shared.RunCommand("mount", v.getRootfsDevFile(), v.rootfsDir, "-o", "defaults,subvol=/@") + case "ext4": + err = shared.RunCommand("mount", v.getRootfsDevFile(), v.rootfsDir) + } + + return err } -func (v *vm) mountUEFIPartition() error { +func (v *vm) mountUEFIFilesystem() error { if v.loopDevice == "" { return fmt.Errorf("Disk image not mounted") } From fd261a161341397895e96e4b6025d8e81b35c40f Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Wed, 14 Oct 2020 13:55:09 +0200 Subject: [PATCH 05/10] distrobuilder/vm: Add OS type Signed-off-by: Thomas Hipp --- distrobuilder/vm.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/distrobuilder/vm.go b/distrobuilder/vm.go index 7939cfa..18b666d 100644 --- a/distrobuilder/vm.go +++ b/distrobuilder/vm.go @@ -14,6 +14,16 @@ import ( "github.com/lxc/distrobuilder/shared" ) +// OS represents the operating system. +type OS int + +const ( + // OSLinux represents the Linux operating system. + OSLinux OS = iota + // OSWindows represents the Windows operating system. + OSWindows +) + type vm struct { imageFile string loopDevice string From 4baf9d48a12cb0bcb4464899d5f631a4283e148e Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Wed, 14 Oct 2020 14:04:21 +0200 Subject: [PATCH 06/10] distrobuilder: Add OS arg to newVM Signed-off-by: Thomas Hipp --- distrobuilder/main_lxd.go | 9 ++++++++- distrobuilder/vm.go | 21 +++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/distrobuilder/main_lxd.go b/distrobuilder/main_lxd.go index 2e1d67f..e4e9313 100644 --- a/distrobuilder/main_lxd.go +++ b/distrobuilder/main_lxd.go @@ -202,6 +202,13 @@ func (c *cmdLXD) run(cmd *cobra.Command, args []string, overlayDir string) error var mounts []shared.ChrootMount var vmDir string var vm *vm + var targetOS OS + + if c.global.definition.Source.Downloader == "windows" { + targetOS = OSWindows + } else { + targetOS = OSLinux + } if c.flagVM { vmDir = filepath.Join(c.global.flagCacheDir, "vm") @@ -218,7 +225,7 @@ func (c *cmdLXD) run(cmd *cobra.Command, args []string, overlayDir string) error imgFile := filepath.Join(c.global.flagCacheDir, imgFilename) - vm, err = newVM(imgFile, vmDir, c.global.definition.Targets.LXD.VM.Filesystem, c.global.definition.Targets.LXD.VM.Size) + vm, err = newVM(imgFile, vmDir, c.global.definition.Targets.LXD.VM.Filesystem, c.global.definition.Targets.LXD.VM.Size, targetOS) if err != nil { return errors.Wrap(err, "Failed to instanciate VM") } diff --git a/distrobuilder/vm.go b/distrobuilder/vm.go index 18b666d..b601238 100644 --- a/distrobuilder/vm.go +++ b/distrobuilder/vm.go @@ -30,14 +30,27 @@ type vm struct { rootFS string rootfsDir string size uint64 + os OS } -func newVM(imageFile, rootfsDir, fs string, size uint64) (*vm, error) { +func newVM(imageFile, rootfsDir, fs string, size uint64, os OS) (*vm, error) { if fs == "" { - fs = "ext4" + if os == OSLinux { + fs = "ext4" + } else { + fs = "ntfs" + } + } + + var supportedFs []string + + if os == OSLinux { + supportedFs = []string{"btrfs", "ext4"} + } else { + supportedFs = []string{"ntfs"} } - if !lxd.StringInSlice(fs, []string{"btrfs", "ext4"}) { + if !lxd.StringInSlice(fs, supportedFs) { return nil, fmt.Errorf("Unsupported fs: %s", fs) } @@ -45,7 +58,7 @@ func newVM(imageFile, rootfsDir, fs string, size uint64) (*vm, error) { size = 4294967296 } - return &vm{imageFile: imageFile, rootfsDir: rootfsDir, rootFS: fs, size: size}, nil + return &vm{imageFile: imageFile, rootfsDir: rootfsDir, rootFS: fs, size: size, os: os}, nil } func (v *vm) getLoopDev() string { From b19477349558a389191de4925dfd4b4d837d217e Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Wed, 14 Oct 2020 14:09:17 +0200 Subject: [PATCH 07/10] distrobuilder/vm: Add function getDiskUUID Signed-off-by: Thomas Hipp --- distrobuilder/vm.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/distrobuilder/vm.go b/distrobuilder/vm.go index b601238..15049c6 100644 --- a/distrobuilder/vm.go +++ b/distrobuilder/vm.go @@ -308,3 +308,16 @@ func (v *vm) mountUEFIFilesystem() error { return shared.RunCommand("mount", v.getUEFIDevFile(), mountpoint) } + +func (v *vm) getDiskUUID() (string, error) { + if v.loopDevice == "" { + return "", fmt.Errorf("Disk image not mounted") + } + + stdout, err := lxd.RunCommand("blkid", "-s", "PTUUID", "-o", "value", v.loopDevice) + if err != nil { + return "", err + } + + return strings.TrimSpace(stdout), nil +} From a6b2724fa74e84274b3c125507c9852b69f717ae Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Wed, 14 Oct 2020 14:09:48 +0200 Subject: [PATCH 08/10] distrobuilder/chroot: Add function replaceUUID Signed-off-by: Thomas Hipp --- distrobuilder/chroot.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/distrobuilder/chroot.go b/distrobuilder/chroot.go index 242f87e..90df838 100644 --- a/distrobuilder/chroot.go +++ b/distrobuilder/chroot.go @@ -1,11 +1,14 @@ package main import ( + "bytes" "fmt" + "io/ioutil" "os" "path/filepath" "strings" + "github.com/google/uuid" "github.com/pkg/errors" "golang.org/x/sys/unix" @@ -199,3 +202,26 @@ func getOverlay(cacheDir, sourceDir string) (func(), string, error) { return cleanup, overlayDir, nil } + +func replaceUUID(path string, old uuid.UUID, new uuid.UUID) error { + f := func(u uuid.UUID) []byte { + b, err := u.MarshalBinary() + if err != nil { + return nil + } + + out := []byte{b[3], b[2], b[1], b[0], b[5], b[4], b[7], b[6]} + out = append(out, b[8:]...) + + return out + } + + read, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + write := bytes.ReplaceAll(read, f(old), f(new)) + + return ioutil.WriteFile(path, write, 0644) +} From 395c359a7393553629beeb62b20a88c29d2a16ce Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Wed, 14 Oct 2020 14:13:00 +0200 Subject: [PATCH 09/10] shared/definition: Add VM specific targets Signed-off-by: Thomas Hipp --- shared/definition.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/shared/definition.go b/shared/definition.go index dccdde7..f83425c 100644 --- a/shared/definition.go +++ b/shared/definition.go @@ -153,10 +153,20 @@ type DefinitionTargetLXC struct { Config []DefinitionTargetLXCConfig `yaml:"config,omitempty"` } +// DefinitionTargetLXDVMUUID represents the old partition UUIDs which are to be replaced by the newly generated ones. +type DefinitionTargetLXDVMUUID struct { + EFI string `yaml:"efi,omitempty"` + Data string `yaml:"data,omitempty"` + Disk string `yaml:"disk,omitempty"` +} + // DefinitionTargetLXDVM represents LXD VM specific options. type DefinitionTargetLXDVM struct { - Size uint64 `yaml:"size,omitempty"` - Filesystem string `yaml:"filesystem,omitempty"` + Size uint64 `yaml:"size,omitempty"` + Filesystem string `yaml:"filesystem,omitempty"` + BCD string `yaml:"bcd,omitempty"` + MSR string `yaml:"msr,omitempty"` + UUID DefinitionTargetLXDVMUUID `yaml:"uuid,omitempty"` } // DefinitionTargetLXD represents LXD specific options. From d832a657e564d3aa66d8351b9fa6b5c154faab74 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Wed, 14 Oct 2020 14:17:37 +0200 Subject: [PATCH 10/10] distrobuilder: Support building Windows VM images Signed-off-by: Thomas Hipp --- distrobuilder/main.go | 27 ++++++++ distrobuilder/main_lxd.go | 140 +++++++++++++++++++++++++++++++++----- distrobuilder/vm.go | 70 +++++++++++++++---- 3 files changed, 205 insertions(+), 32 deletions(-) diff --git a/distrobuilder/main.go b/distrobuilder/main.go index bc6ac88..b9f78ad 100644 --- a/distrobuilder/main.go +++ b/distrobuilder/main.go @@ -223,6 +223,28 @@ func (c *cmdGlobal) preRunBuild(cmd *cobra.Command, args []string) error { return err } + // Sanity checks for Windows VMs. + if c.definition.Source.Downloader == "windows" { + subcommand := cmd.CalledAs() + + if strings.Contains(subcommand, "lxc") { + return fmt.Errorf("Windows is not supported by LXC") + } + + if subcommand == "build-lxd" || subcommand == "pack-lxd" { + ok, err := cmd.Flags().GetBool("vm") + if err != nil { + return err + } + + if !ok { + return fmt.Errorf("LXD only supports Windows VMs") + } + + c.definition.Targets.Type = "vm" + } + } + // Create cache directory if we also plan on creating LXC or LXD images if !isRunningBuildDir { err = os.MkdirAll(c.flagCacheDir, 0755) @@ -251,6 +273,11 @@ func (c *cmdGlobal) preRunBuild(cmd *cobra.Command, args []string) error { return errors.Wrap(err, "Error while downloading source") } + // Return here as we cannot use chroot for Windows. + if c.definition.Source.Downloader == "windows" { + return nil + } + // Setup the mounts and chroot into the rootfs exitChroot, err := shared.SetupChroot(c.sourceDir, c.definition.Environment, nil) if err != nil { diff --git a/distrobuilder/main_lxd.go b/distrobuilder/main_lxd.go index e4e9313..b2a8cb5 100644 --- a/distrobuilder/main_lxd.go +++ b/distrobuilder/main_lxd.go @@ -2,10 +2,12 @@ package main import ( "fmt" + "net/url" "os" "os/exec" "path/filepath" + "github.com/google/uuid" lxd "github.com/lxc/lxd/shared" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -113,6 +115,11 @@ func (c *cmdLXD) commandPack() *cobra.Command { } func (c *cmdLXD) runPack(cmd *cobra.Command, args []string, overlayDir string) error { + // Return here as we cannot use chroot in Windows. + if c.global.definition.Source.Downloader == "windows" { + return nil + } + // Setup the mounts and chroot into the rootfs exitChroot, err := shared.SetupChroot(overlayDir, c.global.definition.Environment, nil) if err != nil { @@ -255,13 +262,110 @@ func (c *cmdLXD) run(cmd *cobra.Command, args []string, overlayDir string) error if err != nil { return errors.Wrap(err, "Failed to mount root filesystem") } - defer lxd.RunCommand("umount", "-R", vmDir) + defer shared.RunCommand("umount", "-R", vmDir) err = vm.mountUEFIFilesystem() if err != nil { return errors.Wrap(err, "Failed to mount UEFI filesystem") } + // Copy EFI files and BCD to EFI partition, and populate MSR partition. + if targetOS == OSWindows { + // This is just a temporary mountpoint which will be removed later. + targetEFIDir := filepath.Join(vmDir, "boot", "efi") + targetEFIMicrosoftBootDir := filepath.Join(targetEFIDir, "EFI", "Microsoft", "Boot") + targetEFIBootDir := filepath.Join(targetEFIDir, "EFI", "Boot") + + err = os.MkdirAll(targetEFIMicrosoftBootDir, 0755) + if err != nil { + return errors.Wrapf(err, "Failed to create %s", targetEFIMicrosoftBootDir) + } + + err = os.MkdirAll(targetEFIBootDir, 0755) + if err != nil { + return errors.Wrapf(err, "Failed to create %s", targetEFIBootDir) + } + + // Copy EFI directory to boot partition + err = shared.RunCommand("rsync", "-a", "-HA", "--sparse", "--devices", "--checksum", "--numeric-ids", filepath.Join(overlayDir, "Windows", "Boot", "EFI")+"/", targetEFIMicrosoftBootDir) + if err != nil { + return errors.Wrap(err, "Failed to copy EFI data") + } + + // Copy bootx64.efi file + err = shared.RunCommand("rsync", "-a", "-HA", "--sparse", "--devices", "--checksum", "--numeric-ids", filepath.Join(targetEFIMicrosoftBootDir, "bootmgfw.efi"), filepath.Join(targetEFIBootDir, "bootx64.efi")) + if err != nil { + return errors.Wrap(err, "Failed to copy bootx64.efi") + } + + // Copy BCD file + bcdFile, err := url.Parse(c.global.definition.Targets.LXD.VM.BCD) + if err != nil { + return errors.Wrap(err, "Failed to get BCD file") + } + + if bcdFile.Scheme == "file" { + err = shared.RunCommand("rsync", "-a", "-HA", "--sparse", "--devices", "--checksum", "--numeric-ids", bcdFile.Path, filepath.Join(targetEFIMicrosoftBootDir, "BCD")) + if err != nil { + return errors.Wrap(err, "Failed to copy BCD file") + } + } + + // Fix UUIDs in the BCD file + efiPartUUID, err := vm.getUEFIPartitionUUID() + if err != nil { + return errors.Wrap(err, "Failed to get part UUID of EFI partition") + } + + dataPartUUID, err := vm.getRootfsPartitionUUID() + if err != nil { + return errors.Wrap(err, "Failed to get part UUID of rootfs partition") + } + + diskUUID, err := vm.getDiskUUID() + if err != nil { + return errors.Wrap(err, "Failed to get disk UUID") + } + + err = replaceUUID(filepath.Join(targetEFIMicrosoftBootDir, "BCD"), uuid.MustParse(c.global.definition.Targets.LXD.VM.UUID.EFI), uuid.MustParse(efiPartUUID)) + if err != nil { + return errors.Wrap(err, "Failed to replace EFI part UUID") + } + + err = replaceUUID(filepath.Join(targetEFIMicrosoftBootDir, "BCD"), uuid.MustParse(c.global.definition.Targets.LXD.VM.UUID.Data), uuid.MustParse(dataPartUUID)) + if err != nil { + return errors.Wrap(err, "Failed to replace rootfs part UUID") + } + + err = replaceUUID(filepath.Join(targetEFIMicrosoftBootDir, "BCD"), uuid.MustParse(c.global.definition.Targets.LXD.VM.UUID.Disk), uuid.MustParse(diskUUID)) + if err != nil { + return errors.Wrap(err, "Failed to replace rootfs part UUID") + } + + err = shared.RunCommand("umount", "-R", targetEFIDir) + if err != nil { + return errors.Wrap(err, "Failed to unmount UEFI filesystem") + } + + err = os.Remove(targetEFIDir) + if err != nil { + return errors.Wrap(err, "Failed to remove EFI mountpoint") + } + + // Copy MSR (Microsoft Reserved Partition) + msrFile, err := url.Parse(c.global.definition.Targets.LXD.VM.MSR) + if err != nil { + return errors.Wrap(err, "Failed to get MSR file") + } + + if msrFile.Scheme == "file" { + err = shared.RunCommand("dd", fmt.Sprintf("if=%s", msrFile.Path), fmt.Sprintf("of=%s", vm.getMSRDevFile())) + if err != nil { + return errors.Wrap(err, "Failed to copy MSR") + } + } + } + // We cannot use LXD's rsync package as that uses the --delete flag which // causes an issue due to the boot/efi directory being present. err = shared.RunCommand("rsync", "-a", "-HA", "--sparse", "--devices", "--checksum", "--numeric-ids", overlayDir+"/", vmDir) @@ -298,26 +402,28 @@ func (c *cmdLXD) run(cmd *cobra.Command, args []string, overlayDir string) error } } - exitChroot, err := shared.SetupChroot(rootfsDir, - c.global.definition.Environment, mounts) - if err != nil { - return errors.Wrap(err, "Failed to chroot") - } - - // Run post files hook - for _, action := range c.global.definition.GetRunnableActions("post-files", imageTargets) { - err := shared.RunScript(action.Action) + if targetOS == OSLinux { + exitChroot, err := shared.SetupChroot(rootfsDir, + c.global.definition.Environment, mounts) if err != nil { - exitChroot() - return errors.Wrap(err, "Failed to run post-files") + return errors.Wrap(err, "Failed to chroot") + } + + // Run post files hook + for _, action := range c.global.definition.GetRunnableActions("post-files", imageTargets) { + err := shared.RunScript(action.Action) + if err != nil { + exitChroot() + return errors.Wrap(err, "Failed to run post-files") + } } - } - exitChroot() + exitChroot() + } // Unmount VM directory and loop device before creating the image. if c.flagVM { - _, err := lxd.RunCommand("umount", "-R", vmDir) + err := shared.RunCommand("umount", "-R", vmDir) if err != nil { return err } @@ -328,7 +434,7 @@ func (c *cmdLXD) run(cmd *cobra.Command, args []string, overlayDir string) error } } - err = img.Build(c.flagType == "unified", c.flagCompression, c.flagVM) + err := img.Build(c.flagType == "unified", c.flagCompression, c.flagVM) if err != nil { return errors.Wrap(err, "Failed to create LXD image") } @@ -337,7 +443,7 @@ func (c *cmdLXD) run(cmd *cobra.Command, args []string, overlayDir string) error } func (c *cmdLXD) checkVMDependencies() error { - dependencies := []string{"btrfs", "mkfs.ext4", "mkfs.vfat", "qemu-img", "rsync", "sgdisk"} + dependencies := []string{"btrfs", "mkfs.ext4", "mkfs.ntfs", "mkfs.vfat", "qemu-img", "rsync", "sgdisk"} for _, dep := range dependencies { _, err := exec.LookPath(dep) diff --git a/distrobuilder/vm.go b/distrobuilder/vm.go index 15049c6..827c8f4 100644 --- a/distrobuilder/vm.go +++ b/distrobuilder/vm.go @@ -70,7 +70,15 @@ func (v *vm) getRootfsDevFile() string { return "" } - return fmt.Sprintf("%sp2", v.loopDevice) + var partNum int + + if v.os == OSLinux { + partNum = 2 + } else { + partNum = 3 + } + + return fmt.Sprintf("%sp%d", v.loopDevice, partNum) } func (v *vm) getUEFIDevFile() string { @@ -81,6 +89,18 @@ func (v *vm) getUEFIDevFile() string { return fmt.Sprintf("%sp1", v.loopDevice) } +func (v *vm) getMSRDevFile() string { + if v.loopDevice == "" { + return "" + } + + if v.os != OSWindows { + return "" + } + + return fmt.Sprintf("%sp2", v.loopDevice) +} + func (v *vm) createEmptyDiskImage() error { f, err := os.Create(v.imageFile) if err != nil { @@ -104,8 +124,20 @@ func (v *vm) createEmptyDiskImage() error { func (v *vm) createPartitions() error { args := [][]string{ {"--zap-all"}, - {"--new=1::+100M", "-t 1:EF00"}, - {"--new=2::", "-t 2:8300"}, + // EFI system partition (ESP) + {"--new=1::+100M", "-t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B"}, + } + + if v.os == OSLinux { + // Linux partition + args = append(args, []string{"--new=2::", "-t 2:0FC63DAF-8483-4772-8E79-3D69D8477DE4"}) + } else { + args = append(args, + // Microsoft Reserved Partition (MSR) + []string{"--new=2::+128M", "-t 2:E3C9E316-0B5C-4DB8-817D-F92DF00215AE"}, + /// Microsoft basic data partition + []string{"--new=3::", "-t 3:EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"}, + ) } for _, cmd := range args { @@ -163,7 +195,15 @@ func (v *vm) mountImage() error { } if !lxd.PathExists(v.getRootfsDevFile()) { - fields := strings.Split(deviceNumbers[2], ":") + var idx int + + if v.os == OSLinux { + idx = 2 + } else { + idx = 3 + } + + fields := strings.Split(deviceNumbers[idx], ":") major, err := strconv.Atoi(fields[0]) if err != nil { @@ -197,18 +237,15 @@ func (v *vm) umountImage() error { return err } - // Make sure that p1 and p2 are also removed. - if lxd.PathExists(v.getUEFIDevFile()) { - err := os.Remove(v.getUEFIDevFile()) - if err != nil { - return err - } - } + // Make sure that all partitions are removed. + for i := 1; i <= 3; i++ { + partition := fmt.Sprintf("%sp%d", v.loopDevice, i) - if lxd.PathExists(v.getRootfsDevFile()) { - err := os.Remove(v.getRootfsDevFile()) - if err != nil { - return err + if lxd.PathExists(partition) { + err := os.Remove(partition) + if err != nil { + return err + } } } @@ -268,6 +305,8 @@ func (v *vm) createFilesystems() error { err = shared.RunCommand("btrfs", "subvolume", "create", fmt.Sprintf("%s/@", v.rootfsDir)) case "ext4": err = shared.RunCommand("mkfs.ext4", "-F", "-b", "4096", "-i 8192", "-m", "0", "-L", "rootfs", "-E", "resize=536870912", v.getRootfsDevFile()) + case "ntfs": + err = shared.RunCommand("mkfs.ntfs", "-f", "-L", "rootfs", v.getRootfsDevFile()) } if err != nil { return err @@ -288,6 +327,7 @@ func (v *vm) mountRootFilesystem() error { case "btrfs": err = shared.RunCommand("mount", v.getRootfsDevFile(), v.rootfsDir, "-o", "defaults,subvol=/@") case "ext4": + case "ntfs": err = shared.RunCommand("mount", v.getRootfsDevFile(), v.rootfsDir) } From lxc-bot at linuxcontainers.org Wed Oct 14 14:50:18 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Wed, 14 Oct 2020 07:50:18 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: Adds ability route external IPs to OVN NICs Message-ID: <5f87102a.1c69fb81.72deb.c6ccSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 1346 bytes Desc: not available URL: -------------- next part -------------- From 36502eb09df6082b16108e759cd405ce1a8cc170 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 10:13:46 +0100 Subject: [PATCH 01/30] lxd/api/project: Adds restricted.networks.subnets config key Signed-off-by: Thomas Parrott --- lxd/api_project.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lxd/api_project.go b/lxd/api_project.go index 770e9abf65..d70d1a2901 100644 --- a/lxd/api_project.go +++ b/lxd/api_project.go @@ -556,6 +556,7 @@ var projectConfigKeys = map[string]func(value string) error{ "restricted.devices.nic": isEitherAllowOrBlockOrManaged, "restricted.devices.disk": isEitherAllowOrBlockOrManaged, "restricted.networks.uplinks": validate.IsAny, + "restricted.networks.subnets": validate.Optional(validate.IsNetworkList), } func projectValidateConfig(config map[string]string) error { From 5be6eede2f1c35babfabfa7ca03e930aef68ca53 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 10:14:24 +0100 Subject: [PATCH 02/30] lxd/network/driver/physical: Adds ipv4.routes and ipv6.routes config keys Signed-off-by: Thomas Parrott --- lxd/network/driver_physical.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lxd/network/driver_physical.go b/lxd/network/driver_physical.go index bcbcc8cf1f..3d24584924 100644 --- a/lxd/network/driver_physical.go +++ b/lxd/network/driver_physical.go @@ -43,6 +43,8 @@ func (n *physical) Validate(config map[string]string) error { "ipv6.gateway": validate.Optional(validate.IsNetworkAddressCIDRV6), "ipv4.ovn.ranges": validate.Optional(validate.IsNetworkRangeV4List), "ipv6.ovn.ranges": validate.Optional(validate.IsNetworkRangeV6List), + "ipv4.routes": validate.Optional(validate.IsNetworkV4List), + "ipv6.routes": validate.Optional(validate.IsNetworkV6List), "dns.nameservers": validate.Optional(validate.IsNetworkAddressList), "volatile.last_state.created": validate.Optional(validate.IsBool), } From 6a01cbf588a2205d5e867969ac42047f64356491 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 10:14:46 +0100 Subject: [PATCH 03/30] lxd/network/driver/ovn: Adds ipv4.routes and ipv6.routes config keys Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index ca3373c9f4..972ea31828 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -99,6 +99,8 @@ func (n *ovn) Validate(config map[string]string) error { return validate.Optional(validate.IsNetworkAddressCIDRV6)(value) }, "ipv6.dhcp.stateful": validate.Optional(validate.IsBool), + "ipv4.routes": validate.Optional(validate.IsNetworkV4List), + "ipv6.routes": validate.Optional(validate.IsNetworkV6List), "dns.domain": validate.IsAny, "dns.search": validate.IsAny, From cf9f092edb0218164eb37e737bd1d47b5d4a1456 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 14:25:00 +0100 Subject: [PATCH 04/30] lxd/network/network/utils: Adds SubnetContains function Signed-off-by: Thomas Parrott --- lxd/network/network_utils.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go index 31f37282ee..b4d5811898 100644 --- a/lxd/network/network_utils.go +++ b/lxd/network/network_utils.go @@ -1072,3 +1072,29 @@ func InterfaceSetMTU(nic string, mtu string) error { return nil } + +// SubnetContains returns true if outerSubnet contains innerSubnet. +func SubnetContains(outerSubnet *net.IPNet, innerSubnet *net.IPNet) bool { + if outerSubnet == nil || innerSubnet == nil { + return false + } + + if !outerSubnet.Contains(innerSubnet.IP) { + return false + } + + outerOnes, outerBits := outerSubnet.Mask.Size() + innerOnes, innerBits := innerSubnet.Mask.Size() + + // Check number of bits in mask match. + if innerBits != outerBits { + return false + } + + // Check that the inner subnet isn't outside of the outer subnet. + if innerOnes < outerOnes { + return false + } + + return true +} From 1d68e67938d5cf43a7be1a36335dbeb6b309ffa3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 09:40:26 +0100 Subject: [PATCH 05/30] lxd/api/project: Moves projectConfigKeys inside projectValidateConfig and adds state This is so validators can have access to database for validation. Signed-off-by: Thomas Parrott --- lxd/api_project.go | 72 +++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/lxd/api_project.go b/lxd/api_project.go index d70d1a2901..cc46a61f31 100644 --- a/lxd/api_project.go +++ b/lxd/api_project.go @@ -15,6 +15,7 @@ import ( "github.com/lxc/lxd/lxd/operations" projecthelpers "github.com/lxc/lxd/lxd/project" "github.com/lxc/lxd/lxd/response" + "github.com/lxc/lxd/lxd/state" "github.com/lxc/lxd/lxd/util" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" @@ -527,56 +528,55 @@ func isEitherAllowOrBlockOrManaged(value string) error { return validate.IsOneOf(value, []string{"block", "allow", "managed"}) } -// Validate the project configuration -var projectConfigKeys = map[string]func(value string) error{ - "features.profiles": validate.Optional(validate.IsBool), - "features.images": validate.Optional(validate.IsBool), - "features.storage.volumes": validate.Optional(validate.IsBool), - "features.networks": validate.Optional(validate.IsBool), - "limits.containers": validate.Optional(validate.IsUint32), - "limits.virtual-machines": validate.Optional(validate.IsUint32), - "limits.memory": validate.Optional(validate.IsSize), - "limits.processes": validate.Optional(validate.IsUint32), - "limits.cpu": validate.Optional(validate.IsUint32), - "limits.disk": validate.Optional(validate.IsSize), - "limits.networks": validate.Optional(validate.IsUint32), - "restricted": validate.Optional(validate.IsBool), - "restricted.containers.nesting": isEitherAllowOrBlock, - "restricted.containers.lowlevel": isEitherAllowOrBlock, - "restricted.containers.privilege": func(value string) error { - return validate.IsOneOf(value, []string{"allow", "unprivileged", "isolated"}) - }, - "restricted.virtual-machines.lowlevel": isEitherAllowOrBlock, - "restricted.devices.unix-char": isEitherAllowOrBlock, - "restricted.devices.unix-block": isEitherAllowOrBlock, - "restricted.devices.unix-hotplug": isEitherAllowOrBlock, - "restricted.devices.infiniband": isEitherAllowOrBlock, - "restricted.devices.gpu": isEitherAllowOrBlock, - "restricted.devices.usb": isEitherAllowOrBlock, - "restricted.devices.nic": isEitherAllowOrBlockOrManaged, - "restricted.devices.disk": isEitherAllowOrBlockOrManaged, - "restricted.networks.uplinks": validate.IsAny, - "restricted.networks.subnets": validate.Optional(validate.IsNetworkList), -} +func projectValidateConfig(s *state.State, config map[string]string) error { + // Validate the project configuration. + projectConfigKeys := map[string]func(value string) error{ + "features.profiles": validate.Optional(validate.IsBool), + "features.images": validate.Optional(validate.IsBool), + "features.storage.volumes": validate.Optional(validate.IsBool), + "features.networks": validate.Optional(validate.IsBool), + "limits.containers": validate.Optional(validate.IsUint32), + "limits.virtual-machines": validate.Optional(validate.IsUint32), + "limits.memory": validate.Optional(validate.IsSize), + "limits.processes": validate.Optional(validate.IsUint32), + "limits.cpu": validate.Optional(validate.IsUint32), + "limits.disk": validate.Optional(validate.IsSize), + "limits.networks": validate.Optional(validate.IsUint32), + "restricted": validate.Optional(validate.IsBool), + "restricted.containers.nesting": isEitherAllowOrBlock, + "restricted.containers.lowlevel": isEitherAllowOrBlock, + "restricted.containers.privilege": func(value string) error { + return validate.IsOneOf(value, []string{"allow", "unprivileged", "isolated"}) + }, + "restricted.virtual-machines.lowlevel": isEitherAllowOrBlock, + "restricted.devices.unix-char": isEitherAllowOrBlock, + "restricted.devices.unix-block": isEitherAllowOrBlock, + "restricted.devices.unix-hotplug": isEitherAllowOrBlock, + "restricted.devices.infiniband": isEitherAllowOrBlock, + "restricted.devices.gpu": isEitherAllowOrBlock, + "restricted.devices.usb": isEitherAllowOrBlock, + "restricted.devices.nic": isEitherAllowOrBlockOrManaged, + "restricted.devices.disk": isEitherAllowOrBlockOrManaged, + "restricted.networks.uplinks": validate.IsAny, + } -func projectValidateConfig(config map[string]string) error { for k, v := range config { key := k - // User keys are free for all + // User keys are free for all. if strings.HasPrefix(key, "user.") { continue } - // Then validate + // Then validate. validator, ok := projectConfigKeys[key] if !ok { - return fmt.Errorf("Invalid project configuration key: %s", k) + return fmt.Errorf("Invalid project configuration key %q", k) } err := validator(v) if err != nil { - return err + return errors.Wrapf(err, "Invalid project configuration key %q value", k) } } From a39b04aefd94be34b202a5e5b9ea20318103d7f1 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 09:41:05 +0100 Subject: [PATCH 06/30] lxd/api/project: projectValidateConfig usage Signed-off-by: Thomas Parrott --- lxd/api_project.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/api_project.go b/lxd/api_project.go index cc46a61f31..5843d31173 100644 --- a/lxd/api_project.go +++ b/lxd/api_project.go @@ -126,7 +126,7 @@ func projectsPost(d *Daemon, r *http.Request) response.Response { } // Validate the configuration - err = projectValidateConfig(project.Config) + err = projectValidateConfig(d.State(), project.Config) if err != nil { return response.BadRequest(err) } @@ -354,7 +354,7 @@ func projectChange(d *Daemon, project *api.Project, req api.ProjectPut) response } // Validate the configuration. - err := projectValidateConfig(req.Config) + err := projectValidateConfig(d.State(), req.Config) if err != nil { return response.BadRequest(err) } From 25d5a972c8b54b0abc3ca524149de8f1f1069293 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 14:33:27 +0100 Subject: [PATCH 07/30] lxd/api/project: Adds projectValidateRestrictedSubnets function Signed-off-by: Thomas Parrott --- lxd/api_project.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/lxd/api_project.go b/lxd/api_project.go index 5843d31173..4ff05ad8a3 100644 --- a/lxd/api_project.go +++ b/lxd/api_project.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "net" "net/http" "strings" @@ -12,7 +13,9 @@ import ( "github.com/pkg/errors" "github.com/lxc/lxd/lxd/db" + "github.com/lxc/lxd/lxd/network" "github.com/lxc/lxd/lxd/operations" + "github.com/lxc/lxd/lxd/project" projecthelpers "github.com/lxc/lxd/lxd/project" "github.com/lxc/lxd/lxd/response" "github.com/lxc/lxd/lxd/state" @@ -606,3 +609,65 @@ func projectValidateName(name string) error { return nil } + +// projectValidateRestrictedSubnets checks that the project's restricted.networks.subnets are properly formatted +// and are within the specified uplink network's routes. +func projectValidateRestrictedSubnets(s *state.State, value string) error { + for _, subnetRaw := range strings.Split(value, ",") { + subnetParts := strings.SplitN(strings.TrimSpace(subnetRaw), ":", 2) + if len(subnetParts) != 2 { + return fmt.Errorf(`Subnet %q invalid, must be in the format of ":"`, subnetRaw) + } + + uplinkName := subnetParts[0] + subnetStr := subnetParts[1] + + restrictedSubnetIP, restrictedSubnet, err := net.ParseCIDR(subnetStr) + if err != nil { + return err + } + + if restrictedSubnetIP.String() != restrictedSubnet.IP.String() { + return fmt.Errorf("Not an IP network address %q", value) + } + + // Check uplink exists and load config to compare subnets. + _, uplink, err := s.Cluster.GetNetworkInAnyState(project.Default, uplinkName) + if err != nil { + return errors.Wrapf(err, "Invalid uplink network %q", uplinkName) + } + + // Parse uplink route subnets. + var uplinkRoutes []*net.IPNet + for _, k := range []string{"ipv4.routes", "ipv6.routes"} { + if uplink.Config[k] == "" { + continue + } + + routes := strings.Split(uplink.Config[k], ",") + for _, route := range routes { + _, uplinkRoute, err := net.ParseCIDR(strings.TrimSpace(route)) + if err != nil { + return err + } + + uplinkRoutes = append(uplinkRoutes, uplinkRoute) + } + } + + foundMatch := false + // Check that the restricted subnet is within one of the uplink's routes. + for _, uplinkRoute := range uplinkRoutes { + if network.SubnetContains(uplinkRoute, restrictedSubnet) { + foundMatch = true + break + } + } + + if !foundMatch { + return fmt.Errorf("Uplink network %q doesn't contain %q in its routes", uplinkName, restrictedSubnet.String()) + } + } + + return nil +} From 1813e23ea4fb569f47559f3d18273be23eb307ce Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 10:22:41 +0100 Subject: [PATCH 08/30] lxd/api/project: Adds restricted.networks.subnets validation to projectValidateConfig Signed-off-by: Thomas Parrott --- lxd/api_project.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lxd/api_project.go b/lxd/api_project.go index 4ff05ad8a3..6711064cab 100644 --- a/lxd/api_project.go +++ b/lxd/api_project.go @@ -561,6 +561,9 @@ func projectValidateConfig(s *state.State, config map[string]string) error { "restricted.devices.nic": isEitherAllowOrBlockOrManaged, "restricted.devices.disk": isEitherAllowOrBlockOrManaged, "restricted.networks.uplinks": validate.IsAny, + "restricted.networks.subnets": validate.Optional(func(value string) error { + return projectValidateRestrictedSubnets(s, value) + }), } for k, v := range config { From 4b16765bef6fa20a65005b071a50810e13546053 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 14:45:54 +0100 Subject: [PATCH 09/30] doc/projects: Removes trailing full stop Signed-off-by: Thomas Parrott --- doc/projects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/projects.md b/doc/projects.md index b7fa5d8961..5b49c8ccab 100644 --- a/doc/projects.md +++ b/doc/projects.md @@ -40,7 +40,7 @@ restricted.devices.unix-block | string | - | block restricted.devices.unix-char | string | - | block | Prevents use of devices of type "unix-char" restricted.devices.unix-hotplug | string | - | block | Prevents use of devices of type "unix-hotplug" restricted.devices.usb | string | - | block | Prevents use of devices of type "usb" -restricted.networks.uplinks | string | - | block | Comma delimited list of network names that can be used as uplinks for networks in this project. +restricted.networks.uplinks | string | - | block | Comma delimited list of network names that can be used as uplinks for networks in this project restricted.virtual-machines.lowlevel | string | - | block | Prevents use of low-level virtual-machine options like raw.qemu, volatile, etc. Those keys can be set using the lxc tool with: From f267cae779112f52070e64ab8857f7612ad98dc0 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 14:46:07 +0100 Subject: [PATCH 10/30] doc/projects: Adds restricted.networks.subnets Signed-off-by: Thomas Parrott --- doc/projects.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/projects.md b/doc/projects.md index 5b49c8ccab..462864fabb 100644 --- a/doc/projects.md +++ b/doc/projects.md @@ -41,6 +41,7 @@ restricted.devices.unix-char | string | - | block restricted.devices.unix-hotplug | string | - | block | Prevents use of devices of type "unix-hotplug" restricted.devices.usb | string | - | block | Prevents use of devices of type "usb" restricted.networks.uplinks | string | - | block | Comma delimited list of network names that can be used as uplinks for networks in this project +restricted.networks.subnets | string | - | block | Comma delimited list of network subnets from the uplink networks (in the form `:`) that are allocated for use in this project restricted.virtual-machines.lowlevel | string | - | block | Prevents use of low-level virtual-machine options like raw.qemu, volatile, etc. Those keys can be set using the lxc tool with: From 758867661f4fe667e3054e29da7248dd27201ccf Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 14:49:33 +0100 Subject: [PATCH 11/30] api: Adds network_ovn_external_subnets extension Signed-off-by: Thomas Parrott --- doc/api-extensions.md | 9 +++++++++ shared/version/api.go | 1 + 2 files changed, 10 insertions(+) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index a2688afac2..06d84d4570 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -1191,3 +1191,12 @@ to disable compression in rsync while migrating storage pools. Adds support for additional network type `physical` that can be used as an uplink for `ovn` networks. The interface specified by `parent` on the `physical` network will be connected to the `ovn` network's gateway. + +## network\_ovn\_external\_subnets +Adds support for `ovn` networks to use external subnets from uplink networks. + +Introduces the `ipv4.routes` and `ipv6.routes` setting on `physical` networks that defines the external routes +allowed to be used in child OVN networks in their `ipv4.routes.external` and `ipv6.routes.external` settings. + +Introduces the `restricted.networks.subnets` project setting that specifies which external subnets are allowed to +be used by OVN networks inside the project (if not set then all routes defined on the uplink network are allowed). diff --git a/shared/version/api.go b/shared/version/api.go index d5035f7161..9d9da206d9 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -230,6 +230,7 @@ var APIExtensions = []string{ "backup_override_name", "storage_rsync_compression", "network_type_physical", + "network_ovn_external_subnets", } // APIExtensionsCount returns the number of available API extensions. From 70050bcb9d52a1a2cfb646b21b213602718efdf8 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 14:58:35 +0100 Subject: [PATCH 12/30] doc/networks: Adds ipv4.routes and ipv6.routes settings to physical network Signed-off-by: Thomas Parrott --- doc/networks.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/networks.md b/doc/networks.md index cc63de9c11..9573e19130 100644 --- a/doc/networks.md +++ b/doc/networks.md @@ -316,6 +316,8 @@ parent | string | - | - vlan | integer | - | - | The VLAN ID to attach to ipv4.gateway | string | standard mode | - | IPv4 address for the gateway and network (CIDR notation) ipv4.ovn.ranges | string | - | none | Comma separate list of IPv4 ranges to use for child OVN network routers (FIRST-LAST format) +ipv4.routes | string | ipv4 address | - | Comma separated list of additional IPv4 CIDR subnets that can be used with child OVN networks ipv4.routes.external setting ipv6.gateway | string | standard mode | - | IPv6 address for the gateway and network (CIDR notation) ipv6.ovn.ranges | string | - | none | Comma separate list of IPv6 ranges to use for child OVN network routers (FIRST-LAST format) +ipv6.routes | string | ipv6 address | - | Comma separated list of additional IPv6 CIDR subnets that can be used with child OVN networks ipv6.routes.external setting dns.nameservers | string | standard mode | - | List of DNS server IPs on physical network From 9de757896e2a47fa6251b3f20dcb26d7f540c93c Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 15:01:22 +0100 Subject: [PATCH 13/30] doc/networks: Adds ipv4.routes.external and ipv6.routes.external to ovn networks Signed-off-by: Thomas Parrott --- doc/networks.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/networks.md b/doc/networks.md index 9573e19130..3d972ddf72 100644 --- a/doc/networks.md +++ b/doc/networks.md @@ -297,8 +297,10 @@ bridge.mtu | integer | - | 1442 dns.domain | string | - | lxd | Domain to advertise to DHCP clients and use for DNS resolution dns.search | string | - | - | Full comma separated domain search list, defaulting to `dns.domain` value ipv4.address | string | standard mode | random unused subnet | IPv4 address for the bridge (CIDR notation). Use "none" to turn off IPv4 or "auto" to generate a new one +ipv4.routes.external | string | ipv4 address | - | Comma separated list of additional external IPv4 CIDR subnets that are allowed for OVN NICs ipv4.routes.external setting ipv6.address | string | standard mode | random unused subnet | IPv6 address for the bridge (CIDR notation). Use "none" to turn off IPv6 or "auto" to generate a new one ipv6.dhcp.stateful | boolean | ipv6 dhcp | false | Whether to allocate addresses using DHCP +ipv6.routes.external | string | ipv6 address | - | Comma separated list of additional external IPv6 CIDR subnets that are allowed for OVN NICs ipv6.routes.external setting network | string | - | - | Uplink network to use for external network access ## network: physical From f656a1c51a561049b86e3132e1e8c37a4119aad5 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 17:30:14 +0100 Subject: [PATCH 14/30] lxd/network/driver/ovn: Updates Validate to check network exists and checks external IP routes Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 124 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 6 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 972ea31828..98f09594a8 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -80,8 +80,13 @@ func (n *ovn) Info() Info { // Validate network config. func (n *ovn) Validate(config map[string]string) error { + // Cache the uplink network for validating "network", "ipv4.routes.external" and "ipv6.routes.external". + _, uplink, uplinkErr := n.state.Cluster.GetNetworkInAnyState(project.Default, config["network"]) + rules := map[string]func(value string) error{ - "network": validate.IsAny, // Is validated during setup. + "network": func(value string) error { + return uplinkErr // Check the pre-lookup of uplink network succeeded. + }, "bridge.hwaddr": validate.Optional(validate.IsNetworkMAC), "bridge.mtu": validate.Optional(validate.IsNetworkMTU), "ipv4.address": func(value string) error { @@ -98,11 +103,11 @@ func (n *ovn) Validate(config map[string]string) error { return validate.Optional(validate.IsNetworkAddressCIDRV6)(value) }, - "ipv6.dhcp.stateful": validate.Optional(validate.IsBool), - "ipv4.routes": validate.Optional(validate.IsNetworkV4List), - "ipv6.routes": validate.Optional(validate.IsNetworkV6List), - "dns.domain": validate.IsAny, - "dns.search": validate.IsAny, + "ipv6.dhcp.stateful": validate.Optional(validate.IsBool), + "ipv4.routes.external": validate.Optional(validate.IsNetworkV4List), + "ipv6.routes.external": validate.Optional(validate.IsNetworkV6List), + "dns.domain": validate.IsAny, + "dns.search": validate.IsAny, // Volatile keys populated automatically as needed. ovnVolatileUplinkIPv4: validate.Optional(validate.IsNetworkAddressV4), @@ -114,6 +119,113 @@ func (n *ovn) Validate(config map[string]string) error { return err } + // Composite checks. + + // Check IP routes are within the uplink network's routes and project's subnet restrictions. + if config["ipv4.routes.external"] != "" || config["ipv6.routes.external"] != "" { + // Load the project to get uplink network restrictions. + var project *api.Project + err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error { + project, err = tx.GetProject(n.project) + if err != nil { + return err + } + + return nil + }) + if err != nil { + return errors.Wrapf(err, "Failed to load IP route restrictions for project %q", n.project) + } + + // Parse uplink route subnets. + var uplinkRoutes []*net.IPNet + for _, k := range []string{"ipv4.routes", "ipv6.routes"} { + if uplink.Config[k] == "" { + continue + } + + routes := strings.Split(uplink.Config[k], ",") + for _, route := range routes { + _, uplinkRoute, err := net.ParseCIDR(strings.TrimSpace(route)) + if err != nil { + return err + } + + uplinkRoutes = append(uplinkRoutes, uplinkRoute) + } + } + + // Parse project's restricted subnets. + var projectRestrictedSubnets []*net.IPNet // Nil value indicates not restricted. + if shared.IsTrue(project.Config["restricted"]) && project.Config["restricted.networks.subnets"] != "" { + projectRestrictedSubnets = []*net.IPNet{} // Empty slice indicates no allowed subnets. + + for _, subnetRaw := range strings.Split(project.Config["restricted.networks.subnets"], ",") { + subnetParts := strings.SplitN(strings.TrimSpace(subnetRaw), ":", 2) + if len(subnetParts) != 2 { + return fmt.Errorf(`Project subnet %q invalid, must be in the format of ":"`, subnetRaw) + } + + uplinkName := subnetParts[0] + subnetStr := subnetParts[1] + + if uplinkName != uplink.Name { + continue // Only include subnets for our uplink. + } + + _, restrictedSubnet, err := net.ParseCIDR(subnetStr) + if err != nil { + return err + } + + projectRestrictedSubnets = append(projectRestrictedSubnets, restrictedSubnet) + } + } + + // Parse and validate our routes. + for _, k := range []string{"ipv4.routes.external", "ipv6.routes.external"} { + if config[k] == "" { + continue + } + + for _, route := range strings.Split(config[k], ",") { + route = strings.TrimSpace(route) + _, routeSubnet, err := net.ParseCIDR(route) + if err != nil { + return err + } + + // Check that the route is within the project's restricted subnets if restricted. + if projectRestrictedSubnets != nil { + foundMatch := false + for _, projectRestrictedSubnet := range projectRestrictedSubnets { + if SubnetContains(projectRestrictedSubnet, routeSubnet) { + foundMatch = true + break + } + } + + if !foundMatch { + return fmt.Errorf("Project %q doesn't contain %q in its restricted uplink subnets", project.Name, routeSubnet.String()) + } + } + + // Check that the route is within the uplink network's routes. + foundMatch := false + for _, uplinkRoute := range uplinkRoutes { + if SubnetContains(uplinkRoute, routeSubnet) { + foundMatch = true + break + } + } + + if !foundMatch { + return fmt.Errorf("Uplink network %q doesn't contain %q in its routes", uplink.Name, routeSubnet.String()) + } + } + } + } + return nil } From 23c2e7bd15df4682241520a0150a930efc4ef0f9 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 13 Oct 2020 17:55:00 +0100 Subject: [PATCH 15/30] doc/instances: Adds ovn NIC documentation Signed-off-by: Thomas Parrott --- doc/instances.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/instances.md b/doc/instances.md index 042195c732..8ceb0cb911 100644 --- a/doc/instances.md +++ b/doc/instances.md @@ -258,6 +258,7 @@ LXD supports different kind of network devices: - [bridged](#nictype-bridged): Uses an existing bridge on the host and creates a virtual device pair to connect the host bridge to the instance. - [macvlan](#nictype-macvlan): Sets up a new network device based on an existing one but using a different MAC address. - [ipvlan](#nictype-ipvlan): Sets up a new network device based on an existing one using the same MAC address but a different IP. + - [ovn](#nictype-ovn): Uses an existing OVN network and creates a virtual device pair to connect the instance to it. - [p2p](#nictype-p2p): Creates a virtual device pair, putting one side in the instance and leaving the other side on the host. - [sriov](#nictype-sriov): Passes a virtual function of an SR-IOV enabled physical network device into the instance. - [routed](#nictype-routed): Creates a virtual device pair to connect the host to the instance and sets up static routes and proxy ARP/NDP entries to allow the instance to join the network of a designated parent interface. @@ -402,6 +403,26 @@ ipv4.routes | string | - | no | Comma deli ipv6.routes | string | - | no | Comma delimited list of IPv6 static routes to add on host to nic boot.priority | integer | - | no | Boot priority for VMs (higher boots first) +#### nictype: ovn + +Supported instance types: container, VM + +Uses an existing OVN network and creates a virtual device pair to connect the instance to it. + +Device configuration properties: + +Key | Type | Default | Required | Description +:-- | :-- | :-- | :-- | :-- +network | string | - | yes | The LXD network to link device to +name | string | kernel assigned | no | The name of the interface inside the instance +host\_name | string | randomly assigned | no | The name of the interface inside the host +hwaddr | string | randomly assigned | no | The MAC address of the new interface +ipv4.address | string | - | no | An IPv4 address to assign to the instance through DHCP +ipv6.address | string | - | no | An IPv6 address to assign to the instance through DHCP +ipv4.routes.external | string | - | no | Comma delimited list of IPv4 static routes to route to the NIC and publish on uplink network +ipv6.routes.external | string | - | no | Comma delimited list of IPv6 static routes to route to the NIC and publish on uplink network +boot.priority | integer | - | no | Boot priority for VMs (higher boots first) + #### nictype: sriov Supported instance types: container, VM From 41888aa1a76bf3868b3e27aed5dbfd6f92898046 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 10:47:04 +0100 Subject: [PATCH 16/30] network ovn external routes validation Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 98f09594a8..10ae4050e9 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -121,7 +121,7 @@ func (n *ovn) Validate(config map[string]string) error { // Composite checks. - // Check IP routes are within the uplink network's routes and project's subnet restrictions. + // Check IP external routes are within the uplink network's routes and project's subnet restrictions. if config["ipv4.routes.external"] != "" || config["ipv6.routes.external"] != "" { // Load the project to get uplink network restrictions. var project *api.Project @@ -182,7 +182,7 @@ func (n *ovn) Validate(config map[string]string) error { } } - // Parse and validate our routes. + // Parse and validate our external routes. for _, k := range []string{"ipv4.routes.external", "ipv6.routes.external"} { if config[k] == "" { continue @@ -210,7 +210,7 @@ func (n *ovn) Validate(config map[string]string) error { } } - // Check that the route is within the uplink network's routes. + // Check that the external route is within the uplink network's routes. foundMatch := false for _, uplinkRoute := range uplinkRoutes { if SubnetContains(uplinkRoute, routeSubnet) { From 04014f5463e3cb09dfb454eb21fe392d69cb05e8 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 10:57:48 +0100 Subject: [PATCH 17/30] lxd/network/driver/ovn: Adds DNS revert to instanceDevicePortAdd Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 10ae4050e9..5bd86a11af 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1877,6 +1877,8 @@ func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, deviceN return "", err } + revert.Add(func() { client.LogicalSwitchPortDeleteDNS(n.getIntSwitchName(), instancePortName) }) + revert.Success() return instancePortName, nil } From 2a52cec2cb83c70adbcf24092a7769ecebf8f178 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 11:23:58 +0100 Subject: [PATCH 18/30] lxd/network/openvswitch/ovn: Adds LogicalRouterRouteDelete function Signed-off-by: Thomas Parrott --- lxd/network/openvswitch/ovn.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go index e0a0d0dc44..ffc2af8d72 100644 --- a/lxd/network/openvswitch/ovn.go +++ b/lxd/network/openvswitch/ovn.go @@ -161,6 +161,23 @@ func (o *OVN) LogicalRouterRouteAdd(routerName OVNRouter, destination *net.IPNet return nil } +// LogicalRouterRouteDelete deletes a static route from the logical router. +// If nextHop is specified as nil, then any route matching the destination is removed. +func (o *OVN) LogicalRouterRouteDelete(routerName OVNRouter, destination *net.IPNet, nextHop net.IP) error { + args := []string{"--if-exists", "lr-route-del", string(routerName), destination.String()} + + if nextHop != nil { + args = append(args, nextHop.String()) + } + + _, err := o.nbctl(args...) + if err != nil { + return err + } + + return nil +} + // LogicalRouterPortAdd adds a named logical router port to a logical router. func (o *OVN) LogicalRouterPortAdd(routerName OVNRouter, portName OVNRouterPort, mac net.HardwareAddr, ipAddr ...*net.IPNet) error { args := []string{"lrp-add", string(routerName), string(portName), mac.String()} From 68777ceab75b59370fdbdda3cda2c525111abe92 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 11:24:14 +0100 Subject: [PATCH 19/30] lxd/network/openvswitch/ovn: Updates LogicalSwitchPortSetDNS to return IPs used for DNS records Signed-off-by: Thomas Parrott --- lxd/network/openvswitch/ovn.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go index ffc2af8d72..23ff565950 100644 --- a/lxd/network/openvswitch/ovn.go +++ b/lxd/network/openvswitch/ovn.go @@ -644,7 +644,8 @@ func (o *OVN) LogicalSwitchPortDynamicIPs(portName OVNSwitchPort) ([]net.IP, err // LogicalSwitchPortSetDNS sets up the switch DNS records for the DNS name resolving to the IPs of the switch port. // Attempts to find at most one IP for each IP protocol, preferring static addresses over dynamic. -func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPort, dnsName string) error { +// Returns the IPv4 and IPv6 addresses used for DNS records. +func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPort, dnsName string) (net.IP, net.IP, error) { var dnsIPv4, dnsIPv6 net.IP // checkAndStoreIP checks if the supplied IP is valid and can be used for a missing DNS IP variable. @@ -667,7 +668,7 @@ func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPo // Get static and dynamic IPs for switch port. staticAddressesRaw, err := o.nbctl("lsp-get-addresses", string(portName)) if err != nil { - return err + return nil, nil, err } staticAddresses := strings.Split(strings.TrimSpace(staticAddressesRaw), " ") @@ -691,7 +692,7 @@ func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPo if hasDynamic && (dnsIPv4 == nil || dnsIPv6 == nil) { dynamicIPs, err := o.LogicalSwitchPortDynamicIPs(portName) if err != nil { - return err + return nil, nil, err } for _, dynamicIP := range dynamicIPs { @@ -719,7 +720,7 @@ func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPo fmt.Sprintf("external_ids:lxd_switch_port=%s", string(portName)), ) if err != nil { - return err + return nil, nil, err } cmdArgs := []string{ @@ -733,13 +734,13 @@ func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPo // Update existing record if exists. _, err = o.nbctl(append([]string{"set", "dns", dnsUUID}, cmdArgs...)...) if err != nil { - return err + return nil, nil, err } } else { // Create new record if needed. dnsUUID, err = o.nbctl(append([]string{"create", "dns"}, cmdArgs...)...) if err != nil { - return err + return nil, nil, err } dnsUUID = strings.TrimSpace(dnsUUID) } @@ -747,10 +748,10 @@ func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPo // Add DNS record to switch DNS records. _, err = o.nbctl("add", "logical_switch", string(switchName), "dns_records", dnsUUID) if err != nil { - return err + return nil, nil, err } - return nil + return dnsIPv4, dnsIPv6, nil } // LogicalSwitchPortDeleteDNS removes DNS records for a switch port. From 5c12ba529d10af42853d9794a6eb817ed899b37d Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 9 Oct 2020 09:25:31 +0100 Subject: [PATCH 20/30] lxd/network/openvswitch/ovn: Adds LogicalRouterDNATSNATAdd function Signed-off-by: Thomas Parrott --- lxd/network/openvswitch/ovn.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go index 23ff565950..be71c1e854 100644 --- a/lxd/network/openvswitch/ovn.go +++ b/lxd/network/openvswitch/ovn.go @@ -151,6 +151,26 @@ func (o *OVN) LogicalRouterSNATAdd(routerName OVNRouter, intNet *net.IPNet, extI return nil } +// LogicalRouterDNATSNATAdd adds a DNAT and SNAT rule to a logical router to translate packets from extIP to intIP. +func (o *OVN) LogicalRouterDNATSNATAdd(routerName OVNRouter, extIP net.IP, intIP net.IP, stateless bool, mayExist bool) error { + args := []string{} + + if mayExist { + args = append(args, "--may-exist") + } + + if stateless { + args = append(args, "--stateless") + } + + _, err := o.nbctl(append(args, "lr-nat-add", string(routerName), "dnat_and_snat", extIP.String(), intIP.String())...) + if err != nil { + return err + } + + return nil +} + // LogicalRouterRouteAdd adds a static route to the logical router. func (o *OVN) LogicalRouterRouteAdd(routerName OVNRouter, destination *net.IPNet, nextHop net.IP) error { _, err := o.nbctl("lr-route-add", string(routerName), destination.String(), nextHop.String()) From d7218955942b3d5f5a8b4966a88dac3f9e5a9436 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 13:16:23 +0100 Subject: [PATCH 21/30] lxd/network/openvswitch/ovn: Adds LogicalRouterDNATSNATDelete function Signed-off-by: Thomas Parrott --- lxd/network/openvswitch/ovn.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go index be71c1e854..5a474973f1 100644 --- a/lxd/network/openvswitch/ovn.go +++ b/lxd/network/openvswitch/ovn.go @@ -171,6 +171,16 @@ func (o *OVN) LogicalRouterDNATSNATAdd(routerName OVNRouter, extIP net.IP, intIP return nil } +// LogicalRouterDNATSNATDelete deletes a DNAT and SNAT rule from a logical router. +func (o *OVN) LogicalRouterDNATSNATDelete(routerName OVNRouter, extIP net.IP) error { + _, err := o.nbctl("--if-exists", "lr-nat-del", string(routerName), "dnat_and_snat", extIP.String()) + if err != nil { + return err + } + + return nil +} + // LogicalRouterRouteAdd adds a static route to the logical router. func (o *OVN) LogicalRouterRouteAdd(routerName OVNRouter, destination *net.IPNet, nextHop net.IP) error { _, err := o.nbctl("lr-route-add", string(routerName), destination.String(), nextHop.String()) From 20c6b125e84cb74c6f3f26653f0dda196dedcea9 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 13:16:38 +0100 Subject: [PATCH 22/30] lxd/network/openvswitch/ovn: Updates LogicalRouterRouteAdd to support mayExist argument Signed-off-by: Thomas Parrott --- lxd/network/openvswitch/ovn.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go index 5a474973f1..bb08c505af 100644 --- a/lxd/network/openvswitch/ovn.go +++ b/lxd/network/openvswitch/ovn.go @@ -182,8 +182,15 @@ func (o *OVN) LogicalRouterDNATSNATDelete(routerName OVNRouter, extIP net.IP) er } // LogicalRouterRouteAdd adds a static route to the logical router. -func (o *OVN) LogicalRouterRouteAdd(routerName OVNRouter, destination *net.IPNet, nextHop net.IP) error { - _, err := o.nbctl("lr-route-add", string(routerName), destination.String(), nextHop.String()) +func (o *OVN) LogicalRouterRouteAdd(routerName OVNRouter, destination *net.IPNet, nextHop net.IP, mayExist bool) error { + args := []string{} + + if mayExist { + args = append(args, "--may-exist") + } + + args = append(args, "lr-route-add", string(routerName), destination.String(), nextHop.String()) + _, err := o.nbctl(args...) if err != nil { return err } From 94f4c06c0db2d372ba114758241d811d74edb7e3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 13:17:13 +0100 Subject: [PATCH 23/30] lxd/network/driver/ovn: client.LogicalRouterRouteAdd usage Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 5bd86a11af..d077c7ffa4 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1343,14 +1343,14 @@ func (n *ovn) setup(update bool) error { // Add default routes. if uplinkNet.routerExtGwIPv4 != nil { - err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}, uplinkNet.routerExtGwIPv4) + err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}, uplinkNet.routerExtGwIPv4, false) if err != nil { return errors.Wrapf(err, "Failed adding IPv4 default route") } } if uplinkNet.routerExtGwIPv6 != nil { - err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}, uplinkNet.routerExtGwIPv6) + err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}, uplinkNet.routerExtGwIPv6, false) if err != nil { return errors.Wrapf(err, "Failed adding IPv6 default route") } From 3843a667eb10b388b72beaef9ecb0773eaaab1de Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 11:24:38 +0100 Subject: [PATCH 24/30] lxd/network/network/utils/ovn: Updates OVNInstanceDevicePortAdd to take an externalRoutes argument Signed-off-by: Thomas Parrott --- lxd/network/network_utils_ovn.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/network/network_utils_ovn.go b/lxd/network/network_utils_ovn.go index df30df80a2..30a2e02b9a 100644 --- a/lxd/network/network_utils_ovn.go +++ b/lxd/network/network_utils_ovn.go @@ -9,14 +9,14 @@ import ( // OVNInstanceDevicePortAdd adds a logical port to the OVN network's internal switch and returns the logical // port name for use linking an OVS port on the integration bridge to the logical switch port. -func OVNInstanceDevicePortAdd(network Network, instanceID int, instanceName string, deviceName string, mac net.HardwareAddr, ips []net.IP) (openvswitch.OVNSwitchPort, error) { +func OVNInstanceDevicePortAdd(network Network, instanceID int, instanceName string, deviceName string, mac net.HardwareAddr, ips []net.IP, externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error) { // Check network is of type OVN. n, ok := network.(*ovn) if !ok { return "", fmt.Errorf("Network is not OVN type") } - return n.instanceDevicePortAdd(instanceID, instanceName, deviceName, mac, ips) + return n.instanceDevicePortAdd(instanceID, instanceName, deviceName, mac, ips, externalRoutes) } // OVNInstanceDevicePortDynamicIPs gets a logical port's dynamic IPs stored in the OVN network's internal switch. From a0203c8132af90831b024379c68e2baa0005eaa3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 12:01:22 +0100 Subject: [PATCH 25/30] lxd/network/network/utils/ovn: Updates OVNInstanceDevicePortDelete to accept an externalRoutes argument So we can clean up external routes on port delete. Signed-off-by: Thomas Parrott --- lxd/network/network_utils_ovn.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/network/network_utils_ovn.go b/lxd/network/network_utils_ovn.go index 30a2e02b9a..948ca6cb4d 100644 --- a/lxd/network/network_utils_ovn.go +++ b/lxd/network/network_utils_ovn.go @@ -31,12 +31,12 @@ func OVNInstanceDevicePortDynamicIPs(network Network, instanceID int, deviceName } // OVNInstanceDevicePortDelete deletes a logical port from the OVN network's internal switch. -func OVNInstanceDevicePortDelete(network Network, instanceID int, deviceName string) error { +func OVNInstanceDevicePortDelete(network Network, instanceID int, deviceName string, externalRoutes []*net.IPNet) error { // Check network is of type OVN. n, ok := network.(*ovn) if !ok { return fmt.Errorf("Network is not OVN type") } - return n.instanceDevicePortDelete(instanceID, deviceName) + return n.instanceDevicePortDelete(instanceID, deviceName, externalRoutes) } From aa594ec8eea223b20d6325324bd7e2d4d3df84bb Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 12:02:05 +0100 Subject: [PATCH 26/30] lxd/network/driver/ovn: Adds externalRoutes support to instanceDevicePortAdd Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 41 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index d077c7ffa4..b2c5f18128 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1813,7 +1813,7 @@ func (n *ovn) getInstanceDevicePortName(instanceID int, deviceName string) openv } // instanceDevicePortAdd adds an instance device port to the internal logical switch and returns the port name. -func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, deviceName string, mac net.HardwareAddr, ips []net.IP) (openvswitch.OVNSwitchPort, error) { +func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, deviceName string, mac net.HardwareAddr, ips []net.IP, externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error) { var dhcpV4ID, dhcpv6ID string revert := revert.New() @@ -1872,13 +1872,50 @@ func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, deviceN return "", err } - err = client.LogicalSwitchPortSetDNS(n.getIntSwitchName(), instancePortName, fmt.Sprintf("%s.%s", instanceName, n.getDomainName())) + dnsIPv4, dnsIPv6, err := client.LogicalSwitchPortSetDNS(n.getIntSwitchName(), instancePortName, fmt.Sprintf("%s.%s", instanceName, n.getDomainName())) if err != nil { return "", err } revert.Add(func() { client.LogicalSwitchPortDeleteDNS(n.getIntSwitchName(), instancePortName) }) + // Add each external route (using the IPs set for DNS as target). + for _, externalRoute := range externalRoutes { + targetIP := dnsIPv4 + if externalRoute.IP.To4() == nil { + targetIP = dnsIPv6 + } + + if targetIP == nil { + return "", fmt.Errorf("Cannot add static route for %q as target IP is not set", externalRoute.String()) + } + + err = client.LogicalRouterRouteAdd(n.getRouterName(), externalRoute, targetIP, true) + if err != nil { + return "", err + } + + revert.Add(func() { client.LogicalRouterRouteDelete(n.getRouterName(), externalRoute, targetIP) }) + + // In order to advertise the external route to the uplink network using proxy ARP/NDP we need to + // add a stateless dnat_and_snat rule (as to my knowledge this is the only way to get the OVN + // router to respond to ARP/NDP requests for IPs that it doesn't actually have). However we have + // to add each IP in the external route individually as DNAT doesn't support whole subnets. + err = SubnetIterate(externalRoute, func(ip net.IP) error { + err = client.LogicalRouterDNATSNATAdd(n.getRouterName(), ip, ip, true, true) + if err != nil { + return err + } + + revert.Add(func() { client.LogicalRouterDNATSNATDelete(n.getRouterName(), ip) }) + + return nil + }) + if err != nil { + return "", err + } + } + revert.Success() return instancePortName, nil } From 03df2f2ecd639be4ba06382f3b84898df2e820b0 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 12:02:20 +0100 Subject: [PATCH 27/30] lxd/network/driver/ovn: Delete externalRoutes in instanceDevicePortDelete Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index b2c5f18128..e6e5b88644 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1933,7 +1933,7 @@ func (n *ovn) instanceDevicePortDynamicIPs(instanceID int, deviceName string) ([ } // instanceDevicePortDelete deletes an instance device port from the internal logical switch. -func (n *ovn) instanceDevicePortDelete(instanceID int, deviceName string) error { +func (n *ovn) instanceDevicePortDelete(instanceID int, deviceName string, externalRoutes []*net.IPNet) error { instancePortName := n.getInstanceDevicePortName(instanceID, deviceName) client, err := n.getClient() @@ -1951,6 +1951,27 @@ func (n *ovn) instanceDevicePortDelete(instanceID int, deviceName string) error return err } + // Delete each external route. + for _, externalRoute := range externalRoutes { + err = client.LogicalRouterRouteDelete(n.getRouterName(), externalRoute, nil) + if err != nil { + return err + } + + // Remove the DNAT rules. + err = SubnetIterate(externalRoute, func(ip net.IP) error { + err = client.LogicalRouterDNATSNATDelete(n.getRouterName(), ip) + if err != nil { + return err + } + + return nil + }) + if err != nil { + return err + } + } + return nil } From dc8fa863b9c1b3319975a5366a29be1b325e562a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 11:25:24 +0100 Subject: [PATCH 28/30] lxd/device/nic: Adds ipv4.routes.external and ipv6.routes.external to nicValidationRules Signed-off-by: Thomas Parrott --- lxd/device/nic.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lxd/device/nic.go b/lxd/device/nic.go index 8bd313b93d..3aa3d164c2 100644 --- a/lxd/device/nic.go +++ b/lxd/device/nic.go @@ -34,6 +34,8 @@ func nicValidationRules(requiredFields []string, optionalFields []string) map[st "ipv6.host_address": validate.Optional(validate.IsNetworkAddressV6), "ipv4.host_table": validate.Optional(validate.IsUint32), "ipv6.host_table": validate.Optional(validate.IsUint32), + "ipv4.routes.external": validate.Optional(validate.IsNetworkV4List), + "ipv6.routes.external": validate.Optional(validate.IsNetworkV6List), } validators := map[string]func(value string) error{} From 74d99b4d722800a66fbd2e0f632033653496854a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 12:02:51 +0100 Subject: [PATCH 29/30] lxd/device/nic/ovn: Adds support for ipv4.routes.external and ipv6.routes.external Signed-off-by: Thomas Parrott --- lxd/device/nic_ovn.go | 98 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 9 deletions(-) diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go index e499c7cf90..d12fa9dfb2 100644 --- a/lxd/device/nic_ovn.go +++ b/lxd/device/nic_ovn.go @@ -4,6 +4,7 @@ import ( "fmt" "net" "os" + "strings" "github.com/mdlayher/netx/eui64" "github.com/pkg/errors" @@ -56,6 +57,8 @@ func (d *nicOVN) validateConfig(instConf instance.ConfigReader) error { "mtu", "ipv4.address", "ipv6.address", + "ipv4.routes.external", + "ipv6.routes.external", "boot.priority", } @@ -125,6 +128,55 @@ func (d *nicOVN) validateConfig(instConf instance.ConfigReader) error { } } + // Check IP external routes are within the network's external routes. + if d.config["ipv4.routes.external"] != "" || d.config["ipv6.routes.external"] != "" { + // Parse network external route subnets. + var networkRoutes []*net.IPNet + for _, k := range []string{"ipv4.routes.external", "ipv6.routes.external"} { + if netConfig[k] == "" { + continue + } + + routes := strings.Split(netConfig[k], ",") + for _, route := range routes { + _, networkRoute, err := net.ParseCIDR(strings.TrimSpace(route)) + if err != nil { + return err + } + + networkRoutes = append(networkRoutes, networkRoute) + } + } + + // Parse and validate our external routes. + for _, k := range []string{"ipv4.routes.external", "ipv6.routes.external"} { + if d.config[k] == "" { + continue + } + + for _, route := range strings.Split(d.config[k], ",") { + route = strings.TrimSpace(route) + _, routeSubnet, err := net.ParseCIDR(route) + if err != nil { + return err + } + + // Check that the external route is within the network's routes. + foundMatch := false + for _, networkRoute := range networkRoutes { + if network.SubnetContains(networkRoute, routeSubnet) { + foundMatch = true + break + } + } + + if !foundMatch { + return fmt.Errorf("Network %q doesn't contain %q in its external routes", n.Name(), routeSubnet.String()) + } + } + } + } + // Apply network level config options to device config before validation. d.config["mtu"] = fmt.Sprintf("%s", netConfig["bridge.mtu"]) @@ -226,22 +278,37 @@ func (d *nicOVN) Start() (*deviceConfig.RunConfig, error) { ips := []net.IP{} for _, key := range []string{"ipv4.address", "ipv6.address"} { - if d.config[key] != "" { - ip := net.ParseIP(d.config[key]) - if ip == nil { - return nil, fmt.Errorf("Invalid %s value %q", key, d.config[key]) - } - ips = append(ips, ip) + if d.config[key] == "" { + continue + } + + ip := net.ParseIP(d.config[key]) + if ip == nil { + return nil, fmt.Errorf("Invalid %s value %q", key, d.config[key]) + } + ips = append(ips, ip) + } + + externalRoutes := []*net.IPNet{} + for _, key := range []string{"ipv4.routes.external", "ipv6.routes.external"} { + if d.config[key] == "" { + continue + } + + _, externalRoute, err := net.ParseCIDR(d.config[key]) + if err != nil { + return nil, errors.Wrapf(err, "Invalid %s value %q", key, d.config[key]) } + externalRoutes = append(externalRoutes, externalRoute) } // Add new OVN logical switch port for instance. - logicalPortName, err := network.OVNInstanceDevicePortAdd(d.network, d.inst.ID(), d.inst.Name(), d.name, mac, ips) + logicalPortName, err := network.OVNInstanceDevicePortAdd(d.network, d.inst.ID(), d.inst.Name(), d.name, mac, ips, externalRoutes) if err != nil { return nil, errors.Wrapf(err, "Failed adding OVN port") } - revert.Add(func() { network.OVNInstanceDevicePortDelete(d.network, d.inst.ID(), d.name) }) + revert.Add(func() { network.OVNInstanceDevicePortDelete(d.network, d.inst.ID(), d.name, externalRoutes) }) // Attach host side veth interface to bridge. integrationBridge, err := d.getIntegrationBridgeName() @@ -347,7 +414,20 @@ func (d *nicOVN) Stop() (*deviceConfig.RunConfig, error) { PostHooks: []func() error{d.postStop}, } - err := network.OVNInstanceDevicePortDelete(d.network, d.inst.ID(), d.name) + externalRoutes := []*net.IPNet{} + for _, key := range []string{"ipv4.routes.external", "ipv6.routes.external"} { + if d.config[key] == "" { + continue + } + + _, externalRoute, err := net.ParseCIDR(d.config[key]) + if err != nil { + return nil, errors.Wrapf(err, "Invalid %s value %q", key, d.config[key]) + } + externalRoutes = append(externalRoutes, externalRoute) + } + + err := network.OVNInstanceDevicePortDelete(d.network, d.inst.ID(), d.name, externalRoutes) if err != nil { // Don't fail here as we still want the postStop hook to run to clean up the local veth pair. d.logger.Error("Failed to remove OVN device port", log.Ctx{"err": err}) From daecef34cf83bcab283103be23f3313fdacbe54e Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 14 Oct 2020 15:16:44 +0100 Subject: [PATCH 30/30] lxd/network/network/utils: Adds SubnetIterate function Signed-off-by: Thomas Parrott --- lxd/network/network_utils.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go index b4d5811898..7adc0e2fd0 100644 --- a/lxd/network/network_utils.go +++ b/lxd/network/network_utils.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "fmt" "io/ioutil" + "math/big" "math/rand" "net" "os" @@ -1098,3 +1099,35 @@ func SubnetContains(outerSubnet *net.IPNet, innerSubnet *net.IPNet) bool { return true } + +// SubnetIterate iterates through each IP in a subnet calling a function for each IP. +// If the ipFunc returns a non-nil error then the iteration stops and the error is returned. +func SubnetIterate(subnet *net.IPNet, ipFunc func(ip net.IP) error) error { + inc := big.NewInt(1) + + // Convert route start IP to native representations to allow incrementing. + startIP := subnet.IP.To4() + if startIP == nil { + startIP = subnet.IP.To16() + } + + startBig := big.NewInt(0) + startBig.SetBytes(startIP) + + // Iterate through IPs in subnet, calling ipFunc for each one. + for { + ip := net.IP(startBig.Bytes()) + if !subnet.Contains(ip) { + break + } + + err := ipFunc(ip) + if err != nil { + return err + } + + startBig.Add(startBig, inc) + } + + return nil +} From lxc-bot at linuxcontainers.org Wed Oct 14 16:23:45 2020 From: lxc-bot at linuxcontainers.org (brauner on Github) Date: Wed, 14 Oct 2020 09:23:45 -0700 (PDT) Subject: [lxc-devel] [lxd/master] forksyscall: use correct function Message-ID: <5f872611.1c69fb81.499a8.0b4aSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 442 bytes Desc: not available URL: -------------- next part -------------- From 00587b8755563027597bc42e2d7d85570f658519 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 14 Oct 2020 18:22:43 +0200 Subject: [PATCH] forksyscall: use correct function Somehow the compiler didn't detect that we called a non-existing function. Signed-off-by: Christian Brauner --- lxd/main_forksyscall.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/main_forksyscall.go b/lxd/main_forksyscall.go index 4b770e73e7..a5b9c565da 100644 --- a/lxd/main_forksyscall.go +++ b/lxd/main_forksyscall.go @@ -35,7 +35,7 @@ import ( extern char* advance_arg(bool required); extern void attach_userns_fd(int ns_fd); extern int pidfd_nsfd(int pidfd, pid_t pid); -extern int preserve_ns(const int pid, const char *ns); +extern int preserve_ns(pid_t pid, int ns_fd, const char *ns); extern bool change_namespaces(int pidfd, int nsfd, unsigned int flags); static bool chdirchroot_in_mntns(int cwd_fd, int root_fd) @@ -371,7 +371,7 @@ static void mount_emulate(void) data = advance_arg(false); } - mnt_fd = preserve_ns(getpid(), "mnt"); + mnt_fd = preserve_ns(getpid(), ns_fd, "mnt"); if (mnt_fd < 0) _exit(EXIT_FAILURE); From lxc-bot at linuxcontainers.org Wed Oct 14 22:24:11 2020 From: lxc-bot at linuxcontainers.org (TimRots on Github) Date: Wed, 14 Oct 2020 15:24:11 -0700 (PDT) Subject: [lxc-devel] [lxd/master] shared/util.go: use string method with stdout and stderr Message-ID: <5f877a8b.1c69fb81.20aba.f637SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 375 bytes Desc: not available URL: -------------- next part -------------- From 17b76d350d2cdbbae294ee1d8fe14332b43dd414 Mon Sep 17 00:00:00 2001 From: Tim Rots Date: Thu, 15 Oct 2020 00:14:21 +0200 Subject: [PATCH] shared/util.go: use string method with stdout and stderr some small changes: * use String() method with stdout and stderr * simplified IsTrue function --- shared/util.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/shared/util.go b/shared/util.go index 8b5bb9f4db..ed0bc08553 100644 --- a/shared/util.go +++ b/shared/util.go @@ -649,11 +649,7 @@ func Uint64InSlice(key uint64, list []uint64) bool { } func IsTrue(value string) bool { - if StringInSlice(strings.ToLower(value), []string{"true", "1", "yes", "on"}) { - return true - } - - return false + return StringInSlice(strings.ToLower(value), []string{"true", "1", "yes", "on"}) } // StringMapHasStringKey returns true if any of the supplied keys are present in the map. @@ -898,15 +894,15 @@ func RunCommandSplit(env []string, filesInherit []*os.File, name string, arg ... err := cmd.Run() if err != nil { err := RunError{ - msg: fmt.Sprintf("Failed to run: %s %s: %s", name, strings.Join(arg, " "), strings.TrimSpace(string(stderr.Bytes()))), - Stdout: string(stdout.Bytes()), - Stderr: string(stderr.Bytes()), + msg: fmt.Sprintf("Failed to run: %s %s: %s", name, strings.Join(arg, " "), strings.TrimSpace(stderr.String())), + Stdout: stdout.String(), + Stderr: stderr.String(), Err: err, } - return string(stdout.Bytes()), string(stderr.Bytes()), err + return stdout.String(), stderr.String(), err } - return string(stdout.Bytes()), string(stderr.Bytes()), nil + return stdout.String(), stderr.String(), nil } // RunCommand runs a command with optional arguments and returns stdout. If the command fails to From lxc-bot at linuxcontainers.org Wed Oct 14 23:17:03 2020 From: lxc-bot at linuxcontainers.org (TimRots on Github) Date: Wed, 14 Oct 2020 16:17:03 -0700 (PDT) Subject: [lxc-devel] [lxd/master] simplestreams.go: remove unneeded fmt.Sprintf and simplify getImages() Message-ID: <5f8786ef.1c69fb81.bdca.7358SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 520 bytes Desc: not available URL: -------------- next part -------------- From b86ec451a5eda61ad35c52825fb826a4bb76966f Mon Sep 17 00:00:00 2001 From: Tim Rots Date: Thu, 15 Oct 2020 01:06:28 +0200 Subject: [PATCH] simplestreams.go: remove unneeded fmt.Sprintf and simplify getImages() * use time.Since() instead of time.Now().Sub() for readbility * entry.Name is already string type, no need for fmt.Sprintf * remove unneeded for-loop in getImages() Signed-off-by: Tim Rots --- shared/simplestreams/simplestreams.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/shared/simplestreams/simplestreams.go b/shared/simplestreams/simplestreams.go index fd420326a3..4d623f6eb2 100644 --- a/shared/simplestreams/simplestreams.go +++ b/shared/simplestreams/simplestreams.go @@ -82,7 +82,7 @@ func (s *SimpleStreams) readCache(path string) ([]byte, bool) { return nil, false } - expired := time.Now().Sub(fi.ModTime()) > s.cacheExpiry + expired := time.Since(fi.ModTime()) > s.cacheExpiry return body, expired } @@ -248,7 +248,7 @@ func (s *SimpleStreams) applyAliases(images []api.Image) ([]api.Image, []extende for _, entry := range aliases { // Short - alias := addAlias(image.Type, image.Architecture, fmt.Sprintf("%s", entry.Name), image.Fingerprint) + alias := addAlias(image.Type, image.Architecture, entry.Name, image.Fingerprint) if alias != nil && architectureName == image.Architecture { image.Aliases = append(image.Aliases, *alias) } @@ -298,10 +298,7 @@ func (s *SimpleStreams) getImages() ([]api.Image, []extendedAlias, error) { } streamImages, _ := products.ToLXD() - - for _, image := range streamImages { - images = append(images, image) - } + images = append(images, streamImages...) } // Setup the aliases From lxc-bot at linuxcontainers.org Thu Oct 15 08:39:19 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Thu, 15 Oct 2020 01:39:19 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Instance: Updates templateApplyNow to close files at end of each iteration Message-ID: <5f880ab7.1c69fb81.b1406.09b4SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 498 bytes Desc: not available URL: -------------- next part -------------- From 6ffef1d5a7cd31671e1595fa4670fe58570acb1b Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 15 Oct 2020 09:37:57 +0100 Subject: [PATCH] lxd/instance/drivers: Updates templateApplyNow to close files at end of each iteration By using an anonymous function so that defer runs at end of each iteration. Signed-off-by: Thomas Parrott --- lxd/instance/drivers/driver_lxc.go | 124 +++++++++++++++------------- lxd/instance/drivers/driver_qemu.go | 99 +++++++++++----------- 2 files changed, 119 insertions(+), 104 deletions(-) diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go index ac307d6d1e..7e716b9d7d 100644 --- a/lxd/instance/drivers/driver_lxc.go +++ b/lxd/instance/drivers/driver_lxc.go @@ -5119,81 +5119,89 @@ func (c *lxc) templateApplyNow(trigger string) error { // Go through the templates for tplPath, tpl := range metadata.Templates { - var w *os.File + err = func(tplPath string, tpl *api.ImageMetadataTemplate) error { + var w *os.File + + // Check if the template should be applied now + found := false + for _, tplTrigger := range tpl.When { + if tplTrigger == trigger { + found = true + break + } + } - // Check if the template should be applied now - found := false - for _, tplTrigger := range tpl.When { - if tplTrigger == trigger { - found = true - break + if !found { + return nil } - } - if !found { - continue - } + // Open the file to template, create if needed + fullpath := filepath.Join(c.RootfsPath(), strings.TrimLeft(tplPath, "/")) + if shared.PathExists(fullpath) { + if tpl.CreateOnly { + return nil + } - // Open the file to template, create if needed - fullpath := filepath.Join(c.RootfsPath(), strings.TrimLeft(tplPath, "/")) - if shared.PathExists(fullpath) { - if tpl.CreateOnly { - continue + // Open the existing file + w, err = os.Create(fullpath) + if err != nil { + return errors.Wrap(err, "Failed to create template file") + } + } else { + // Create the directories leading to the file + shared.MkdirAllOwner(path.Dir(fullpath), 0755, int(rootUID), int(rootGID)) + + // Create the file itself + w, err = os.Create(fullpath) + if err != nil { + return err + } + + // Fix ownership and mode + w.Chown(int(rootUID), int(rootGID)) + w.Chmod(0644) } + defer w.Close() - // Open the existing file - w, err = os.Create(fullpath) + // Read the template + tplString, err := ioutil.ReadFile(filepath.Join(c.TemplatesPath(), tpl.Template)) if err != nil { - return errors.Wrap(err, "Failed to create template file") + return errors.Wrap(err, "Failed to read template file") } - } else { - // Create the directories leading to the file - shared.MkdirAllOwner(path.Dir(fullpath), 0755, int(rootUID), int(rootGID)) - // Create the file itself - w, err = os.Create(fullpath) + // Restrict filesystem access to within the container's rootfs + tplSet := pongo2.NewSet(fmt.Sprintf("%s-%s", c.name, tpl.Template), template.ChrootLoader{Path: c.RootfsPath()}) + + tplRender, err := tplSet.FromString("{% autoescape off %}" + string(tplString) + "{% endautoescape %}") if err != nil { - return err + return errors.Wrap(err, "Failed to render template") } - // Fix ownership and mode - w.Chown(int(rootUID), int(rootGID)) - w.Chmod(0644) - } - defer w.Close() - - // Read the template - tplString, err := ioutil.ReadFile(filepath.Join(c.TemplatesPath(), tpl.Template)) - if err != nil { - return errors.Wrap(err, "Failed to read template file") - } + configGet := func(confKey, confDefault *pongo2.Value) *pongo2.Value { + val, ok := c.expandedConfig[confKey.String()] + if !ok { + return confDefault + } - // Restrict filesystem access to within the container's rootfs - tplSet := pongo2.NewSet(fmt.Sprintf("%s-%s", c.name, tpl.Template), template.ChrootLoader{Path: c.RootfsPath()}) + return pongo2.AsValue(strings.TrimRight(val, "\r\n")) + } - tplRender, err := tplSet.FromString("{% autoescape off %}" + string(tplString) + "{% endautoescape %}") - if err != nil { - return errors.Wrap(err, "Failed to render template") - } + // Render the template + tplRender.ExecuteWriter(pongo2.Context{"trigger": trigger, + "path": tplPath, + "container": containerMeta, + "instance": containerMeta, + "config": c.expandedConfig, + "devices": c.expandedDevices, + "properties": tpl.Properties, + "config_get": configGet}, w) - configGet := func(confKey, confDefault *pongo2.Value) *pongo2.Value { - val, ok := c.expandedConfig[confKey.String()] - if !ok { - return confDefault - } + return nil - return pongo2.AsValue(strings.TrimRight(val, "\r\n")) + }(tplPath, tpl) + if err != nil { + return err } - - // Render the template - tplRender.ExecuteWriter(pongo2.Context{"trigger": trigger, - "path": tplPath, - "container": containerMeta, - "instance": containerMeta, - "config": c.expandedConfig, - "devices": c.expandedDevices, - "properties": tpl.Properties, - "config_get": configGet}, w) } return nil diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 4fb7f8a37f..1abaddbbb2 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -1534,62 +1534,69 @@ func (vm *qemu) templateApplyNow(trigger string, path string) error { // Go through the templates. for tplPath, tpl := range metadata.Templates { - var w *os.File - - // Check if the template should be applied now. - found := false - for _, tplTrigger := range tpl.When { - if tplTrigger == trigger { - found = true - break + err = func(tplPath string, tpl *api.ImageMetadataTemplate) error { + var w *os.File + + // Check if the template should be applied now. + found := false + for _, tplTrigger := range tpl.When { + if tplTrigger == trigger { + found = true + break + } } - } - if !found { - continue - } + if !found { + return nil + } - // Create the file itself. - w, err = os.Create(filepath.Join(path, fmt.Sprintf("%s.out", tpl.Template))) - if err != nil { - return err - } + // Create the file itself. + w, err = os.Create(filepath.Join(path, fmt.Sprintf("%s.out", tpl.Template))) + if err != nil { + return err + } - // Fix ownership and mode. - w.Chmod(0644) - defer w.Close() + // Fix ownership and mode. + w.Chmod(0644) + defer w.Close() - // Read the template. - tplString, err := ioutil.ReadFile(filepath.Join(vm.TemplatesPath(), tpl.Template)) - if err != nil { - return errors.Wrap(err, "Failed to read template file") - } + // Read the template. + tplString, err := ioutil.ReadFile(filepath.Join(vm.TemplatesPath(), tpl.Template)) + if err != nil { + return errors.Wrap(err, "Failed to read template file") + } - // Restrict filesystem access to within the container's rootfs. - tplSet := pongo2.NewSet(fmt.Sprintf("%s-%s", vm.name, tpl.Template), pongoTemplate.ChrootLoader{Path: vm.TemplatesPath()}) - tplRender, err := tplSet.FromString("{% autoescape off %}" + string(tplString) + "{% endautoescape %}") - if err != nil { - return errors.Wrap(err, "Failed to render template") - } + // Restrict filesystem access to within the container's rootfs. + tplSet := pongo2.NewSet(fmt.Sprintf("%s-%s", vm.name, tpl.Template), pongoTemplate.ChrootLoader{Path: vm.TemplatesPath()}) + tplRender, err := tplSet.FromString("{% autoescape off %}" + string(tplString) + "{% endautoescape %}") + if err != nil { + return errors.Wrap(err, "Failed to render template") + } + + configGet := func(confKey, confDefault *pongo2.Value) *pongo2.Value { + val, ok := vm.expandedConfig[confKey.String()] + if !ok { + return confDefault + } - configGet := func(confKey, confDefault *pongo2.Value) *pongo2.Value { - val, ok := vm.expandedConfig[confKey.String()] - if !ok { - return confDefault + return pongo2.AsValue(strings.TrimRight(val, "\r\n")) } - return pongo2.AsValue(strings.TrimRight(val, "\r\n")) - } + // Render the template. + tplRender.ExecuteWriter(pongo2.Context{"trigger": trigger, + "path": tplPath, + "instance": instanceMeta, + "container": instanceMeta, // FIXME: remove once most images have moved away. + "config": vm.expandedConfig, + "devices": vm.expandedDevices, + "properties": tpl.Properties, + "config_get": configGet}, w) - // Render the template. - tplRender.ExecuteWriter(pongo2.Context{"trigger": trigger, - "path": tplPath, - "instance": instanceMeta, - "container": instanceMeta, // FIXME: remove once most images have moved away. - "config": vm.expandedConfig, - "devices": vm.expandedDevices, - "properties": tpl.Properties, - "config_get": configGet}, w) + return nil + }(tplPath, tpl) + if err != nil { + return err + } } return nil From lxc-bot at linuxcontainers.org Thu Oct 15 08:57:59 2020 From: lxc-bot at linuxcontainers.org (brauner on Github) Date: Thu, 15 Oct 2020 01:57:59 -0700 (PDT) Subject: [lxc-devel] [lxd/master] seccomp: bugfixes Message-ID: <5f880f17.1c69fb81.faa5e.8adbSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 364 bytes Desc: not available URL: -------------- next part -------------- From 79d6da7cd0634e13227672189923ed905131d563 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 15 Oct 2020 09:40:27 +0200 Subject: [PATCH 1/3] forkmount: improve Signed-off-by: Christian Brauner --- lxd/main_forkmount.go | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/lxd/main_forkmount.go b/lxd/main_forkmount.go index 2e32756b91..a34d3957ea 100644 --- a/lxd/main_forkmount.go +++ b/lxd/main_forkmount.go @@ -329,17 +329,14 @@ static void do_lxc_forkumount(void) void forkmount(void) { - char *cur = NULL; - - char *command = NULL; + char *command = NULL, *cur = NULL; + int ns_fd = -EBADF, pidfd = -EBADF; pid_t pid = 0; - int ns_fd = -EBADF, pidfd; // Get the subcommand command = advance_arg(false); - if (command == NULL || (strcmp(command, "--help") == 0 || strcmp(command, "--version") == 0 || strcmp(command, "-h") == 0)) { - return; - } + if (command == NULL || (strcmp(command, "--help") == 0 || strcmp(command, "--version") == 0 || strcmp(command, "-h") == 0)) + _exit(EXIT_SUCCESS); // Check that we're root if (geteuid() != 0) { @@ -354,9 +351,9 @@ void forkmount(void) if (strcmp(command, "lxd-mount") == 0) { // Get the pid cur = advance_arg(false); - if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, "--version") == 0 || strcmp(cur, "-h") == 0)) { - return; - } + if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, "--version") == 0 || strcmp(cur, "-h") == 0)) + _exit(EXIT_SUCCESS); + pid = atoi(cur); if (pid <= 0) _exit(EXIT_FAILURE); @@ -372,9 +369,9 @@ void forkmount(void) } else if (strcmp(command, "lxd-umount") == 0) { // Get the pid cur = advance_arg(false); - if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, "--version") == 0 || strcmp(cur, "-h") == 0)) { - return; - } + if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, "--version") == 0 || strcmp(cur, "-h") == 0)) + _exit(EXIT_SUCCESS); + pid = atoi(cur); if (pid <= 0) _exit(EXIT_FAILURE); From 317ab6bc5285d849f0c30e35d7f5e73ca519faff Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 15 Oct 2020 10:56:29 +0200 Subject: [PATCH 2/3] seccomp: improve logging for the seccomp notifier Signed-off-by: Christian Brauner --- lxd/seccomp/seccomp.go | 53 ++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/lxd/seccomp/seccomp.go b/lxd/seccomp/seccomp.go index e424b31b4b..699fdf85fb 100644 --- a/lxd/seccomp/seccomp.go +++ b/lxd/seccomp/seccomp.go @@ -947,6 +947,7 @@ retry: return fmt.Errorf("Failed to send full response to seccomp client %v", siov.ucred.Pid) } + logger.Debugf("Send seccomp notification for id(%d)", siov.resp.id) return nil } @@ -1186,11 +1187,14 @@ func (s *Server) doDeviceSyscall(c Instance, args *MknodArgs, siov *Iovec) int { // HandleMknodSyscall handles a mknod syscall. func (s *Server) HandleMknodSyscall(c Instance, siov *Iovec) int { ctx := log.Ctx{"container": c.Name(), - "project": c.Project(), - "syscall_number": siov.req.data.nr, - "audit_architecture": siov.req.data.arch, - "seccomp_notify_id": siov.req.id, - "seccomp_notify_flags": siov.req.flags, + "project": c.Project(), + "syscall_number": siov.req.data.nr, + "audit_architecture": siov.req.data.arch, + "seccomp_notify_id": siov.req.id, + "seccomp_notify_flags": siov.req.flags, + "seccomp_notify_pid": siov.req.pid, + "seccomp_notify_fd": siov.notifyFd, + "seccomp_notify_mem_fd": siov.memFd, } defer logger.Debug("Handling mknod syscall", ctx) @@ -1233,11 +1237,14 @@ func (s *Server) HandleMknodSyscall(c Instance, siov *Iovec) int { // HandleMknodatSyscall handles a mknodat syscall. func (s *Server) HandleMknodatSyscall(c Instance, siov *Iovec) int { ctx := log.Ctx{"container": c.Name(), - "project": c.Project(), - "syscall_number": siov.req.data.nr, - "audit_architecture": siov.req.data.arch, - "seccomp_notify_id": siov.req.id, - "seccomp_notify_flags": siov.req.flags, + "project": c.Project(), + "syscall_number": siov.req.data.nr, + "audit_architecture": siov.req.data.arch, + "seccomp_notify_id": siov.req.id, + "seccomp_notify_flags": siov.req.flags, + "seccomp_notify_pid": siov.req.pid, + "seccomp_notify_fd": siov.notifyFd, + "seccomp_notify_mem_fd": siov.memFd, } defer logger.Debug("Handling mknodat syscall", ctx) @@ -1309,11 +1316,14 @@ type SetxattrArgs struct { // HandleSetxattrSyscall handles setxattr syscalls. func (s *Server) HandleSetxattrSyscall(c Instance, siov *Iovec) int { ctx := log.Ctx{"container": c.Name(), - "project": c.Project(), - "syscall_number": siov.req.data.nr, - "audit_architecture": siov.req.data.arch, - "seccomp_notify_id": siov.req.id, - "seccomp_notify_flags": siov.req.flags, + "project": c.Project(), + "syscall_number": siov.req.data.nr, + "audit_architecture": siov.req.data.arch, + "seccomp_notify_id": siov.req.id, + "seccomp_notify_flags": siov.req.flags, + "seccomp_notify_pid": siov.req.pid, + "seccomp_notify_fd": siov.notifyFd, + "seccomp_notify_mem_fd": siov.memFd, } defer logger.Debug("Handling setxattr syscall", ctx) @@ -1588,11 +1598,14 @@ func (s *Server) mountHandleHugetlbfsArgs(c Instance, args *MountArgs, nsuid int // HandleMountSyscall handles mount syscalls. func (s *Server) HandleMountSyscall(c Instance, siov *Iovec) int { ctx := log.Ctx{"container": c.Name(), - "project": c.Project(), - "syscall_number": siov.req.data.nr, - "audit_architecture": siov.req.data.arch, - "seccomp_notify_id": siov.req.id, - "seccomp_notify_flags": siov.req.flags, + "project": c.Project(), + "syscall_number": siov.req.data.nr, + "audit_architecture": siov.req.data.arch, + "seccomp_notify_id": siov.req.id, + "seccomp_notify_flags": siov.req.flags, + "seccomp_notify_pid": siov.req.pid, + "seccomp_notify_fd": siov.notifyFd, + "seccomp_notify_mem_fd": siov.memFd, } defer logger.Debug("Handling mount syscall", ctx) From f71ea4c4497a3a58d30f87c0f54beff032aca642 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 15 Oct 2020 10:56:45 +0200 Subject: [PATCH 3/3] seccomp: make sure that insertMountLXD() doesn't call into LXC Signed-off-by: Christian Brauner --- lxd/instance/drivers/driver_lxc.go | 9 +++++++-- lxd/seccomp/seccomp.go | 10 +++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go index ac307d6d1e..c2e57e1803 100644 --- a/lxd/instance/drivers/driver_lxc.go +++ b/lxd/instance/drivers/driver_lxc.go @@ -6066,7 +6066,12 @@ func (c *lxc) unmount() (bool, error) { return unmounted, nil } -// Mount handling +// insertMountLXD inserts a mount into a LXD container. +// This function is used for the seccomp notifier and so cannot call any +// functions that would cause LXC to talk to the container's monitor. Otherwise +// we'll have a deadlock (with a timeout but still). The InitPID() call here is +// the exception since the seccomp notifier will make sure to always pass a +// valid PID. func (c *lxc) insertMountLXD(source, target, fstype string, flags int, mntnsPID int, shiftfs bool) error { pid := mntnsPID if pid <= 0 { @@ -6117,7 +6122,7 @@ func (c *lxc) insertMountLXD(source, target, fstype string, flags int, mntnsPID mntsrc := filepath.Join("/dev/.lxd-mounts", filepath.Base(tmpMount)) pidStr := fmt.Sprintf("%d", pid) - pidFdNr, pidFd := c.inheritInitPidFd() + pidFdNr, pidFd := seccomp.MakePidFd(pid, c.state) if pidFdNr >= 0 { defer pidFd.Close() } diff --git a/lxd/seccomp/seccomp.go b/lxd/seccomp/seccomp.go index 699fdf85fb..7a4553dee2 100644 --- a/lxd/seccomp/seccomp.go +++ b/lxd/seccomp/seccomp.go @@ -656,8 +656,8 @@ func InstanceNeedsIntercept(s *state.State, c Instance) (bool, error) { return needed, nil } -// InheritInitPidFd prepares a pidfd to inherit for the init process of the container. -func inheritPidFd(pid int, s *state.State) (int, *os.File) { +// MakePidFd prepares a pidfd to inherit for the init process of the container. +func MakePidFd(pid int, s *state.State) (int, *os.File) { if s.OS.PidFds { pidFdFile, err := shared.PidFdOpen(pid, 0) if err != nil { @@ -1112,7 +1112,7 @@ func CallForkmknod(c Instance, dev deviceConfig.Device, requestPID int, s *state return int(-C.EPERM) } - pidFdNr, pidFd := inheritPidFd(requestPID, s) + pidFdNr, pidFd := MakePidFd(requestPID, s) if pidFdNr >= 0 { defer pidFd.Close() } @@ -1332,7 +1332,7 @@ func (s *Server) HandleSetxattrSyscall(c Instance, siov *Iovec) int { args.pid = int(siov.req.pid) - pidFdNr, pidFd := inheritPidFd(args.pid, s.s) + pidFdNr, pidFd := MakePidFd(args.pid, s.s) if pidFdNr >= 0 { defer pidFd.Close() } @@ -1615,7 +1615,7 @@ func (s *Server) HandleMountSyscall(c Instance, siov *Iovec) int { shift: s.MountSyscallShift(c), } - pidFdNr, pidFd := inheritPidFd(args.pid, s.s) + pidFdNr, pidFd := MakePidFd(args.pid, s.s) if pidFdNr >= 0 { defer pidFd.Close() } From lxc-bot at linuxcontainers.org Thu Oct 15 08:58:59 2020 From: lxc-bot at linuxcontainers.org (brauner on Github) Date: Thu, 15 Oct 2020 01:58:59 -0700 (PDT) Subject: [lxc-devel] [lxc/master] seccomp: bugfixes Message-ID: <5f880f53.1c69fb81.23238.1276SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 364 bytes Desc: not available URL: -------------- next part -------------- From a76fe490dc301fe1dca160dac34967566c4e1a02 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 15 Oct 2020 09:19:23 +0200 Subject: [PATCH 1/2] seccomp: log invalid seccomp notify ids Signed-off-by: Christian Brauner --- src/lxc/seccomp.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c index 61b9954a86..b19b46f53e 100644 --- a/src/lxc/seccomp.c +++ b/src/lxc/seccomp.c @@ -1377,7 +1377,7 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, int listener_proxy_fd = conf->seccomp.notifier.proxy_fd; struct seccomp_notify_proxy_msg msg = {0}; char *cookie = conf->seccomp.notifier.cookie; - uint64_t req_id; + __u64 req_id; if (events & EPOLLHUP) { lxc_mainloop_del_handler(descr, fd); @@ -1409,6 +1409,7 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, /* remember the ID in case we receive garbage from the proxy */ resp->id = req_id = req->id; + TRACE("Received seccomp notification with id(%llu)", req_id); snprintf(mem_path, sizeof(mem_path), "/proc/%d", req->pid); fd_pid = open(mem_path, O_RDONLY | O_DIRECTORY | O_CLOEXEC); @@ -1433,7 +1434,7 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, ret = seccomp_notify_id_valid(fd, req->id); if (ret < 0) { seccomp_notify_default_answer(fd, req, resp, hdlr); - SYSERROR("Invalid seccomp notify request id"); + SYSERROR("Invalid seccomp notify request id(%llu)", req->id); goto out; } @@ -1492,8 +1493,8 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, } if (resp->id != req_id) { + ERROR("Proxy returned response with illegal id(%llu) != id(%llu)", resp->id, req_id); resp->id = req_id; - ERROR("Proxy returned response with illegal id"); seccomp_notify_default_answer(fd, req, resp, hdlr); goto out; } @@ -1505,9 +1506,16 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, goto out; } + if (resp->id != req_id) { + ERROR("Proxy returned response with illegal id(%llu) != id(%llu)", resp->id, req_id); + resp->id = req_id; + } + ret = seccomp_notify_respond(fd, resp); if (ret) SYSERROR("Failed to send seccomp notification"); + else + TRACE("Sent response for seccomp notification with id(%llu)", resp->id); out: #endif From dc70d7e4fbb731201c3170e3b23079a74ceed6c1 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 15 Oct 2020 10:00:44 +0200 Subject: [PATCH 2/2] seccomp: improve default notification sending Signed-off-by: Christian Brauner --- src/lxc/seccomp.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c index b19b46f53e..8ee68df52e 100644 --- a/src/lxc/seccomp.c +++ b/src/lxc/seccomp.c @@ -1347,9 +1347,14 @@ static void seccomp_notify_default_answer(int fd, struct seccomp_notif *req, { resp->id = req->id; resp->error = -ENOSYS; + resp->val = 0; + resp->flags = 0; if (seccomp_notify_respond(fd, resp)) - SYSERROR("Failed to send default message to seccomp"); + SYSERROR("Failed to send default message to seccomp notification with id(%llu)", resp->id); + else + TRACE("Sent default response for seccomp notification with id(%llu)", resp->id); + memset(resp, 0, handler->conf->seccomp.notifier.sizes.seccomp_notif_resp); } #endif @@ -1385,7 +1390,7 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, return log_trace(0, "Removing seccomp notifier fd %d", fd); } - memset(req, 0, sizeof(*req)); + memset(req, 0, conf->seccomp.notifier.sizes.seccomp_notif); ret = seccomp_notify_receive(fd, req); if (ret) { SYSERROR("Failed to read seccomp notification"); @@ -1516,6 +1521,7 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, SYSERROR("Failed to send seccomp notification"); else TRACE("Sent response for seccomp notification with id(%llu)", resp->id); + memset(resp, 0, conf->seccomp.notifier.sizes.seccomp_notif_resp); out: #endif From lxc-bot at linuxcontainers.org Thu Oct 15 09:00:20 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Thu, 15 Oct 2020 02:00:20 -0700 (PDT) Subject: [lxc-devel] [distrobuilder-pkg-snap/latest-candidate] Set version to 1.1 Message-ID: <5f880fa4.1c69fb81.3c75c.0bdbSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 371 bytes Desc: not available URL: -------------- next part -------------- From 4e10fb834196c5b79b370a05985ad3b05780f94d Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Thu, 15 Oct 2020 10:53:17 +0200 Subject: [PATCH] Set version to 1.1 Signed-off-by: Thomas Hipp --- snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapcraft.yaml b/snapcraft.yaml index ac5bda2..6738446 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -1,6 +1,6 @@ name: distrobuilder base: core18 -version: "1.0" +version: "1.1" grade: stable summary: System container image builder for LXC and LXD description: |- From noreply at github.com Thu Oct 15 09:39:10 2020 From: noreply at github.com (Wolfgang Bumiller) Date: Thu, 15 Oct 2020 02:39:10 -0700 Subject: [lxc-devel] [lxc/lxc] a76fe4: seccomp: log invalid seccomp notify ids Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: a76fe490dc301fe1dca160dac34967566c4e1a02 https://github.com/lxc/lxc/commit/a76fe490dc301fe1dca160dac34967566c4e1a02 Author: Christian Brauner Date: 2020-10-15 (Thu, 15 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- seccomp: log invalid seccomp notify ids Signed-off-by: Christian Brauner Commit: dc70d7e4fbb731201c3170e3b23079a74ceed6c1 https://github.com/lxc/lxc/commit/dc70d7e4fbb731201c3170e3b23079a74ceed6c1 Author: Christian Brauner Date: 2020-10-15 (Thu, 15 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- seccomp: improve default notification sending Signed-off-by: Christian Brauner Commit: eb587451d07873b49e5e573e73e004057875ec64 https://github.com/lxc/lxc/commit/eb587451d07873b49e5e573e73e004057875ec64 Author: Wolfgang Bumiller Date: 2020-10-15 (Thu, 15 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- Merge pull request #3553 from brauner/2020-10-15/seccomp seccomp: bugfixes Compare: https://github.com/lxc/lxc/compare/186ff2beaffa...eb587451d078 From builds at travis-ci.org Thu Oct 15 10:18:43 2020 From: builds at travis-ci.org (Travis CI) Date: Thu, 15 Oct 2020 10:18:43 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7893 (master - eb58745) In-Reply-To: Message-ID: <5f882202e1075_13fb77521d54c6928a@travis-tasks-598fb856cb-jmksf.mail> Build Update for lxc/lxc ------------------------------------- Build: #7893 Status: Errored Duration: 38 mins and 50 secs Commit: eb58745 (master) Author: Wolfgang Bumiller Message: Merge pull request #3553 from brauner/2020-10-15/seccomp seccomp: bugfixes View the changeset: https://github.com/lxc/lxc/compare/186ff2beaffa...eb587451d078 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/735986126?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lxc-bot at linuxcontainers.org Thu Oct 15 16:08:57 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Thu, 15 Oct 2020 09:08:57 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: Fixes inconsistency between normal bridge and fan bridge default ipv4.nat value Message-ID: <5f887419.1c69fb81.b2504.3137SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 481 bytes Desc: not available URL: -------------- next part -------------- From a57eb849b32ecee6aa726493e5d9784576405367 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 15 Oct 2020 17:07:16 +0100 Subject: [PATCH] lxd/network/driver/bridge: Fixes inconsistency between normal bridge and fan bridge default ipv4.nat value Normal bridge (and docs) say if unset, the default is false, however the fan bridge was defaulting to true if unset. Signed-off-by: Thomas Parrott --- lxd/network/driver_bridge.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go index c1c1b6dea0..e0d8779072 100644 --- a/lxd/network/driver_bridge.go +++ b/lxd/network/driver_bridge.go @@ -1169,7 +1169,7 @@ func (n *bridge) setup(oldConfig map[string]string) error { } // Configure NAT. - if n.config["ipv4.nat"] == "" || shared.IsTrue(n.config["ipv4.nat"]) { + if shared.IsTrue(n.config["ipv4.nat"]) { if n.config["ipv4.nat.order"] == "after" { err = n.state.Firewall.NetworkSetupOutboundNAT(n.name, overlaySubnet, nil, true) if err != nil { From lxc-bot at linuxcontainers.org Thu Oct 15 16:31:26 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Thu, 15 Oct 2020 09:31:26 -0700 (PDT) Subject: [lxc-devel] [lxd/master] network: Adds OVN ipv4.nat and ipv6.nat keys Message-ID: <5f88795e.1c69fb81.eb853.dad0SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 501 bytes Desc: not available URL: -------------- next part -------------- From e67455914b5fe60c4a16c7a102518f3358df7ebf Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 15 Oct 2020 17:29:57 +0100 Subject: [PATCH 1/3] api: Adds network_ovn_nat extension Signed-off-by: Thomas Parrott --- doc/api-extensions.md | 8 ++++++++ shared/version/api.go | 1 + 2 files changed, 9 insertions(+) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 06d84d4570..aac2f59bcf 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -1200,3 +1200,11 @@ allowed to be used in child OVN networks in their `ipv4.routes.external` and `ip Introduces the `restricted.networks.subnets` project setting that specifies which external subnets are allowed to be used by OVN networks inside the project (if not set then all routes defined on the uplink network are allowed). + +## network\_ovn\_nat +Adds support for `ipv4.nat` and `ipv6.nat` settings on `ovn` networks. + +When creating the network if these settings are unspecified, and an equivalent IP address is being generated for +the subnet, then the appropriate NAT setting will added set to `true`. + +If the setting is missing then the value is taken as `false`. diff --git a/shared/version/api.go b/shared/version/api.go index 9d9da206d9..b0bada4a3f 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -231,6 +231,7 @@ var APIExtensions = []string{ "storage_rsync_compression", "network_type_physical", "network_ovn_external_subnets", + "network_ovn_nat", } // APIExtensionsCount returns the number of available API extensions. From 69e1cdff5f9b5e64db04ff731863b51c23f69251 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 15 Oct 2020 16:48:03 +0100 Subject: [PATCH 2/3] doc/networks: Adds ipv4.nat and ipv6.nat to OVN networks Signed-off-by: Thomas Parrott --- doc/networks.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/networks.md b/doc/networks.md index 3d972ddf72..6fb54fec4d 100644 --- a/doc/networks.md +++ b/doc/networks.md @@ -317,9 +317,11 @@ mtu | integer | - | - parent | string | - | - | Parent interface to create sriov NICs on vlan | integer | - | - | The VLAN ID to attach to ipv4.gateway | string | standard mode | - | IPv4 address for the gateway and network (CIDR notation) +ipv4.nat | boolean | ipv4 address | false | Whether to NAT (will default to true if unset and a random ipv4.address is generated) ipv4.ovn.ranges | string | - | none | Comma separate list of IPv4 ranges to use for child OVN network routers (FIRST-LAST format) ipv4.routes | string | ipv4 address | - | Comma separated list of additional IPv4 CIDR subnets that can be used with child OVN networks ipv4.routes.external setting ipv6.gateway | string | standard mode | - | IPv6 address for the gateway and network (CIDR notation) +ipv6.nat | boolean | ipv6 address | false | Whether to NAT (will default to true if unset and a random ipv6.address is generated) ipv6.ovn.ranges | string | - | none | Comma separate list of IPv6 ranges to use for child OVN network routers (FIRST-LAST format) ipv6.routes | string | ipv6 address | - | Comma separated list of additional IPv6 CIDR subnets that can be used with child OVN networks ipv6.routes.external setting dns.nameservers | string | standard mode | - | List of DNS server IPs on physical network From 9d52d2f6597aa76df8dc7deb95903d2294cf3a21 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 15 Oct 2020 17:13:00 +0100 Subject: [PATCH 3/3] lxd/network/driver/ovn: Adds ipv4.nat and ipv6.nat support NAT defaults to disabled if these settings are unset. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 0755b02580..a6dc816a7a 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -101,6 +101,8 @@ func (n *ovn) Validate(config map[string]string) error { "ipv6.dhcp.stateful": validate.Optional(validate.IsBool), "ipv4.routes.external": validate.Optional(validate.IsNetworkV4List), "ipv6.routes.external": validate.Optional(validate.IsNetworkV6List), + "ipv4.nat": validate.Optional(validate.IsBool), + "ipv6.nat": validate.Optional(validate.IsBool), "dns.domain": validate.IsAny, "dns.search": validate.IsAny, @@ -1112,6 +1114,10 @@ func (n *ovn) FillConfig(config map[string]string) error { } config["ipv4.address"] = subnet + + if config["ipv4.nat"] == "" { + config["ipv4.nat"] = "true" + } } if config["ipv6.address"] == "auto" { @@ -1121,6 +1127,10 @@ func (n *ovn) FillConfig(config map[string]string) error { } config["ipv6.address"] = subnet + + if config["ipv6.nat"] == "" { + config["ipv6.nat"] = "true" + } } return nil @@ -1373,14 +1383,14 @@ func (n *ovn) setup(update bool) error { } // Add SNAT rules. - if routerIntPortIPv4Net != nil && routerExtPortIPv4 != nil { + if shared.IsTrue(n.config["ipv4.nat"]) && routerIntPortIPv4Net != nil && routerExtPortIPv4 != nil { err = client.LogicalRouterSNATAdd(n.getRouterName(), routerIntPortIPv4Net, routerExtPortIPv4) if err != nil { return err } } - if routerIntPortIPv6Net != nil && routerExtPortIPv6 != nil { + if shared.IsTrue(n.config["ipv6.nat"]) && routerIntPortIPv6Net != nil && routerExtPortIPv6 != nil { err = client.LogicalRouterSNATAdd(n.getRouterName(), routerIntPortIPv6Net, routerExtPortIPv6) if err != nil { return err From lxc-bot at linuxcontainers.org Thu Oct 15 18:41:47 2020 From: lxc-bot at linuxcontainers.org (aither64 on Github) Date: Thu, 15 Oct 2020 11:41:47 -0700 (PDT) Subject: [lxc-devel] [ruby-lxc/master] Fix compatibility with liblxc 4.0.4 and newer Message-ID: <5f8897eb.1c69fb81.c6308.6820SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 748 bytes Desc: not available URL: -------------- next part -------------- From 1a7b7cc4ea6ddf5867f857cc104da46e7acc1908 Mon Sep 17 00:00:00 2001 From: Jakub Skokan Date: Mon, 12 Oct 2020 15:56:48 +0200 Subject: [PATCH 1/2] Remove LXC.arch_to_personality It depended on a function from liblxc, which is no longer exported due to https://github.com/lxc/lxc/commit/63c2a0bf06d5ddd7cbbc908ea1e403b69f4b6a0c --- ext/lxc/lxc.c | 30 ------------------------------ test/test_lxc_class_methods.rb | 5 ----- 2 files changed, 35 deletions(-) diff --git a/ext/lxc/lxc.c b/ext/lxc/lxc.c index b0ec634..1f0b440 100644 --- a/ext/lxc/lxc.c +++ b/ext/lxc/lxc.c @@ -42,7 +42,6 @@ extern void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1, #endif extern int lxc_wait_for_pid_status(pid_t pid); -extern long lxc_config_parse_arch(const char *arch); static VALUE Container; static VALUE Error; @@ -90,33 +89,6 @@ free_c_string_array(char **arr) * container-specific methods are contained in the +LXC::Container+ class. */ -/* - * call-seq: - * LXC.arch_to_personality(arch) - * - * Converts an architecture string (x86, i686, x86_64 or amd64) to a - * "personality", either +:linux32+ or +:linux+, for the 32-bit and 64-bit - * architectures, respectively. - */ -static VALUE -lxc_arch_to_personality(VALUE self, VALUE rb_arch) -{ - int ret; - char *arch; - - arch = StringValuePtr(rb_arch); - ret = lxc_config_parse_arch(arch); - - switch (ret) { - case PER_LINUX32: - return SYMBOL("linux32"); - case PER_LINUX: - return SYMBOL("linux"); - default: - rb_raise(Error, "unknown personality"); - } -} - /* * call-seq: * LXC.run_command(command) @@ -2127,8 +2099,6 @@ Init_lxc(void) { VALUE LXC = rb_define_module("LXC"); - rb_define_singleton_method(LXC, "arch_to_personality", - lxc_arch_to_personality, 1); rb_define_singleton_method(LXC, "run_command", lxc_run_command, 1); rb_define_singleton_method(LXC, "run_shell", lxc_run_shell, 0); rb_define_singleton_method(LXC, "global_config_item", diff --git a/test/test_lxc_class_methods.rb b/test/test_lxc_class_methods.rb index 8ff41b3..ead95d0 100644 --- a/test/test_lxc_class_methods.rb +++ b/test/test_lxc_class_methods.rb @@ -17,9 +17,4 @@ def setup def test_list_containers assert(LXC.list_containers.include?('test')) end - - def test_arch_to_personality - assert_equal(:linux32, LXC.arch_to_personality('x86')) - assert_equal(:linux, LXC.arch_to_personality('x86_64')) - end end From d1381ddb3a30673b7022f968af9d55c569d6c8a8 Mon Sep 17 00:00:00 2001 From: Jakub Skokan Date: Mon, 12 Oct 2020 17:04:27 +0200 Subject: [PATCH 2/2] Reimplement lxc_wait_for_pid_status as it is no longer exported by liblxc --- ext/lxc/lxc.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/ext/lxc/lxc.c b/ext/lxc/lxc.c index 1f0b440..ce14c37 100644 --- a/ext/lxc/lxc.c +++ b/ext/lxc/lxc.c @@ -41,8 +41,6 @@ extern void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1, #define RELEASING_GVL2(func, arg, killfunc, killarg) func(arg) #endif -extern int lxc_wait_for_pid_status(pid_t pid); - static VALUE Container; static VALUE Error; @@ -677,10 +675,30 @@ lxc_attach_parse_options(VALUE rb_opts) return NULL; } +static int +wait_for_pid_status(pid_t pid) +{ + int status, ret; + +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == EINTR) + goto again; + + return -1; + } + + if (ret != pid) + goto again; + + return status; +} + static RETURN_WITHOUT_GVL_TYPE -lxc_wait_for_pid_status_without_gvl(void *pid) +wait_for_pid_status_without_gvl(void *pid) { - RETURN_WITHOUT_GVL(lxc_wait_for_pid_status(*(pid_t*)pid)); + RETURN_WITHOUT_GVL(wait_for_pid_status(*(pid_t*)pid)); } #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) || defined(HAVE_RB_THREAD_BLOCKING_REGION) @@ -748,7 +766,7 @@ container_attach(int argc, VALUE *argv, VALUE self) goto out; if (wait) { - ret = RELEASING_GVL2(lxc_wait_for_pid_status_without_gvl, &pid, + ret = RELEASING_GVL2(wait_for_pid_status_without_gvl, &pid, kill_pid_without_gvl, &pid); /* handle case where attach fails */ if (WIFEXITED(ret) && WEXITSTATUS(ret) == 255) From lxc-bot at linuxcontainers.org Fri Oct 16 02:25:44 2020 From: lxc-bot at linuxcontainers.org (comannnnndooooo on Github) Date: Thu, 15 Oct 2020 19:25:44 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Remade all typo fix changes and reapplied patch. (Continuation of: Pull request: 8022) Message-ID: <5f8904a8.1c69fb81.ba53e.a7dfSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 485 bytes Desc: not available URL: -------------- next part -------------- From a6c990e86186454ebbce28dcf7c792893ed12cb2 Mon Sep 17 00:00:00 2001 From: Kyle Colburn Date: Thu, 15 Oct 2020 21:22:58 -0500 Subject: [PATCH] Remade all typo fix changes and reapplied patch. Signed-off-by: Kyle Colburn --- lxd/db/storage_pools.go | 2 +- lxd/patches.go | 56 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go index e27a3d1e12..76d12693d1 100644 --- a/lxd/db/storage_pools.go +++ b/lxd/db/storage_pools.go @@ -885,7 +885,7 @@ var StoragePoolNodeConfigKeys = []string{ "source", "volatile.initial_source", "zfs.pool_name", - "lvm.thinpool", + "lvm.thinpool_name", "lvm.vg_name", } diff --git a/lxd/patches.go b/lxd/patches.go index d60ad8e1b6..6a37e49214 100644 --- a/lxd/patches.go +++ b/lxd/patches.go @@ -102,6 +102,7 @@ var patches = []patch{ {name: "network_clear_bridge_volatile_hwaddr", stage: patchPostDaemonStorage, run: patchNetworkCearBridgeVolatileHwaddr}, {name: "move_backups_instances", stage: patchPostDaemonStorage, run: patchMoveBackupsInstances}, {name: "network_ovn_enable_nat", stage: patchPostDaemonStorage, run: patchNetworkOVNEnableNAT}, + {name: "thinpool_typo_fix", stage: patchPostDaemonStorage, run: patchThinpoolTypoFix}, } type patch struct { @@ -166,6 +167,61 @@ func patchesApply(d *Daemon, stage patchStage) error { // Patches begin here +// renames any config incorrectly set config file entries due to the lvm.thinpool_name typo +func patchThinpoolTypoFix(name string, d *Daemon) error { + tx, err = d.cluster.Transaction(func(tx *db.ClusterTx) 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 { + // Fetch the config for this lvm pool and check if it has the + // lvn.thinpool_name key. + config, err := query.SelectConfig( + tx, "storage_pools_config", "storage_pool_id=?", poolID) + if err != nil { + return errors.Wrap(err, "Failed a fetch of lvm pool config") + } + + value, ok := config["lvm.thinpool"] + if !ok { + continue + } + + // Delete the current key + _, err = tx.Exec(` + DELETE FROM storage_pools_config WHERE key='lvm.thinpool' AND storage_pool_id=?`, poolID) + if err != nil { + return errors.Wrapf(err, "Failed to delete %s config", key) + } + + // Add the config entry for each node + for _, nodeID := range nodeIDs { + _, err := tx.Exec(` + INSERT INTO storage_pools_config(storage_pool_id, node_id, key, value) + VALUES(?, ?, 'lvm.thinpool_name', ?) + `, poolID, curNodeID, value) + if err != nil { + return errors.Wrapf(err, "Failed to create %s node config", key) + } + } + } + }) + if err != nil { + return errors.Wrap(err, "Failed to commit transaction") + } + + return err +} + // patchNetworkOVNEnableNAT adds "ipv4.nat" and "ipv6.nat" keys set to "true" to OVN networks if not present. // This is to ensure existing networks retain the old behaviour of always having NAT enabled as we introduce // the new NAT settings which default to disabled if not specified. From lxc-bot at linuxcontainers.org Fri Oct 16 06:38:18 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Thu, 15 Oct 2020 23:38:18 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/device/usb: Fix check for required USB device Message-ID: <5f893fda.1c69fb81.86903.80bfSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From f7fd7215344bb0d5d31d04307a0ba5d17e9bf0fc Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 16 Oct 2020 08:37:26 +0200 Subject: [PATCH] lxd/device/usb: Fix check for required USB device This fixes the check for required USB devices for VMs. Signed-off-by: Thomas Hipp --- lxd/device/usb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/device/usb.go b/lxd/device/usb.go index b8d9fe7e07..4c6efea101 100644 --- a/lxd/device/usb.go +++ b/lxd/device/usb.go @@ -172,7 +172,7 @@ func (d *usb) startVM() (*deviceConfig.RunConfig, error) { }...) } - if d.isRequired() && len(runConf.Mounts) <= 0 { + if d.isRequired() && len(runConf.USBDevice) <= 0 { return nil, fmt.Errorf("Required USB device not found") } From lxc-bot at linuxcontainers.org Fri Oct 16 09:29:54 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Fri, 16 Oct 2020 02:29:54 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Add TPM device type Message-ID: <5f896812.1c69fb81.b612c.b628SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 02adc3b7d536473d65c9225e248cc54434535a88 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 16 Oct 2020 11:27:22 +0200 Subject: [PATCH 1/4] lxd/device/config: Add TPMDevice to RunConfig Signed-off-by: Thomas Hipp --- lxd/device/config/device_runconfig.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lxd/device/config/device_runconfig.go b/lxd/device/config/device_runconfig.go index f68d77b27e..e4280392e5 100644 --- a/lxd/device/config/device_runconfig.go +++ b/lxd/device/config/device_runconfig.go @@ -43,4 +43,5 @@ type RunConfig struct { PostHooks []func() error // Functions to be run after device attach/detach. GPUDevice []RunConfigItem // GPU device configuration settings. USBDevice []RunConfigItem // USB device configuration settings. + TPMDevice []RunConfigItem // TPM device configuration settings. } From 7c5bf5ae724f9d99603228c274c10f71f91a4ec8 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 16 Oct 2020 11:25:39 +0200 Subject: [PATCH 2/4] lxd/device: Add TPM device type Signed-off-by: Thomas Hipp --- lxd/device/device_load.go | 2 + lxd/device/tpm.go | 221 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 lxd/device/tpm.go diff --git a/lxd/device/device_load.go b/lxd/device/device_load.go index c89161224c..cf4b521a59 100644 --- a/lxd/device/device_load.go +++ b/lxd/device/device_load.go @@ -66,6 +66,8 @@ func load(inst instance.Instance, state *state.State, projectName string, name s dev = &disk{} case "none": dev = &none{} + case "tpm": + dev = &tpm{} } // Check a valid device type has been found. diff --git a/lxd/device/tpm.go b/lxd/device/tpm.go new file mode 100644 index 0000000000..4a01d5a089 --- /dev/null +++ b/lxd/device/tpm.go @@ -0,0 +1,221 @@ +package device + +import ( + "bufio" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + "golang.org/x/sys/unix" + + deviceConfig "github.com/lxc/lxd/lxd/device/config" + "github.com/lxc/lxd/lxd/instance" + "github.com/lxc/lxd/lxd/instance/instancetype" + "github.com/lxc/lxd/lxd/revert" + "github.com/lxc/lxd/lxd/util" + "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/validate" +) + +type tpm struct { + deviceCommon + emulator *exec.Cmd +} + +// isRequired indicates whether the device config requires this device to start OK. +func (d *tpm) isRequired() bool { + // Defaults to not required. + if shared.IsTrue(d.config["required"]) { + return true + } + + return false +} + +// validateConfig checks the supplied config for correctness. +func (d *tpm) validateConfig(instConf instance.ConfigReader) error { + if !instanceSupported(instConf.Type(), instancetype.Container, instancetype.VM) { + return ErrUnsupportedDevType + } + + rules := map[string]func(string) error{ + "required": validate.Optional(validate.IsBool), + } + + err := d.config.Validate(rules) + if err != nil { + return err + } + + return nil +} + +// validateEnvironment checks if the TPM emulator is available. +func (d *tpm) validateEnvironment() error { + // Validate the required binary. + _, err := exec.LookPath("swtpm") + if err != nil { + return fmt.Errorf("Required tool '%s' is missing", "swtpm") + } + + if d.inst.Type() == instancetype.Container { + // Load module tpm_vtpm_proxy which creates the /dev/vtpmx device, required + // by the TPM emulator. + module := "tpm_vtpm_proxy" + + err := util.LoadModule(module) + if err != nil { + return fmt.Errorf("Failed to load kernel module %q: %s", module, err) + } + } + + return nil +} + +// Start is run when the device is added to the instance. +func (d *tpm) Start() (*deviceConfig.RunConfig, error) { + err := d.validateEnvironment() + if err != nil { + return nil, err + } + + if d.inst.Type() == instancetype.VM { + return d.startVM() + } + + return d.startContainer() +} + +func (d *tpm) startContainer() (*deviceConfig.RunConfig, error) { + tpmPath := filepath.Join(d.inst.Path(), "tpm") + d.emulator = exec.Command("swtpm", "chardev", "--vtpm-proxy", "--tpm2", "--tpmstate", fmt.Sprintf("dir=%s", tpmPath)) + + cmdOut, err := d.emulator.StdoutPipe() + if err != nil { + return nil, err + } + + err = d.emulator.Start() + if err != nil { + return nil, err + } + + revert := revert.New() + defer revert.Fail() + + // Stop the TPM emulator if anything goes wrong. + revert.Add(func() { d.Stop() }) + + var devPath string + var major, minor int + + // Get the device path + scanner := bufio.NewScanner(cmdOut) + for scanner.Scan() { + // The output will be something like: + // New TPM device: /dev/tpm1 (major/minor = 253/1) + // We just need device path and the major/minor numbers. + fields := strings.Split(scanner.Text(), " ") + + if len(fields) < 7 { + return nil, fmt.Errorf("Failed to TPM device information") + } + + devPath = fields[3] + + _, err := fmt.Sscanf(fields[6], "%d/%d)", &major, &minor) + if err != nil { + return nil, err + } + + // Stop reading from stdout as that's the only information we care about. + break + } + + runConf := deviceConfig.RunConfig{} + + err = unixDeviceSetupCharNum(d.state, d.inst.DevicesPath(), "unix", d.name, d.config, uint32(major), uint32(minor), devPath, false, &runConf) + if err != nil { + return nil, err + } + + if d.isRequired() && len(runConf.Mounts) <= 0 { + return nil, fmt.Errorf("Required TPM device not found") + } + + revert.Success() + + return &runConf, nil +} + +func (d *tpm) startVM() (*deviceConfig.RunConfig, error) { + tpmPath := filepath.Join(d.inst.Path(), "tpm") + socketPath := filepath.Join(tpmPath, fmt.Sprintf("swtpm-%s.sock", d.name)) + runConf := deviceConfig.RunConfig{ + TPMDevice: []deviceConfig.RunConfigItem{ + {Key: "devName", Value: d.name}, + {Key: "path", Value: socketPath}, + }, + } + + err := os.MkdirAll(tpmPath, 0755) + if err != nil { + return nil, err + } + + d.emulator = exec.Command("swtpm", "socket", "--ctrl", fmt.Sprintf("type=unixio,path=%s", socketPath), "--tpmstate", fmt.Sprintf("dir=%s", tpmPath), "--tpm2") + + // Start the TPM emulator. + err = d.emulator.Start() + if err != nil { + return nil, err + } + + return &runConf, nil +} + +// Stop terminates the TPM emulator. +func (d *tpm) Stop() (*deviceConfig.RunConfig, error) { + runConf := deviceConfig.RunConfig{ + PostHooks: []func() error{d.postStop}, + } + + if d.inst.Type() == instancetype.Container { + err := unixDeviceRemove(d.inst.DevicesPath(), "unix", d.name, "", &runConf) + if err != nil { + return nil, err + } + + return &runConf, nil + } + + doneChan := make(chan interface{}) + + go func() { + d.emulator.Wait() + doneChan <- nil + }() + + // Terminate the TPM emulator gracefully. + d.emulator.Process.Signal(unix.SIGTERM) + + // Wait for the process to be terminated before cleaning up. Kill it if + // it hasn't terminated within 2 seconds. + select { + case <-doneChan: + case <-time.After(time.Second * 2): + d.emulator.Process.Kill() + close(doneChan) + } + + return &runConf, nil +} + +func (d *tpm) postStop() error { + tpmPath := filepath.Join(d.inst.Path(), "tpm") + + return os.RemoveAll(tpmPath) +} From 3fa4145a57cce46c5f4225b5f182b05c99d2a5ce Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 16 Oct 2020 11:28:03 +0200 Subject: [PATCH 3/4] lxd/db: Add device type "tpm" Signed-off-by: Thomas Hipp --- lxd/db/devices.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lxd/db/devices.go b/lxd/db/devices.go index adbf52d0db..8c402b80cf 100644 --- a/lxd/db/devices.go +++ b/lxd/db/devices.go @@ -28,6 +28,8 @@ func deviceTypeToString(t int) (string, error) { return "proxy", nil case 9: return "unix-hotplug", nil + case 10: + return "tpm", nil default: return "", fmt.Errorf("Invalid device type %d", t) } @@ -55,6 +57,8 @@ func deviceTypeToInt(t string) (int, error) { return 8, nil case "unix-hotplug": return 9, nil + case "tpm": + return 10, nil default: return -1, fmt.Errorf("Invalid device type %s", t) } From caf3fba2161fec0bbd49f07726575197f0360b36 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 16 Oct 2020 11:28:59 +0200 Subject: [PATCH 4/4] lxd/instance/drivers: Support TPM devices in VMs Signed-off-by: Thomas Hipp --- lxd/instance/drivers/driver_qemu.go | 30 +++++++++++++++++++ lxd/instance/drivers/driver_qemu_templates.go | 14 +++++++++ 2 files changed, 44 insertions(+) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 1abaddbbb2..67f665e506 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -1857,6 +1857,15 @@ func (vm *qemu) generateQemuConfigFile(busName string, devConfs []*deviceConfig. return "", err } } + + // Add TPM device. + if len(runConf.TPMDevice) > 0 { + err = vm.addTPMDeviceConfig(sb, runConf.TPMDevice) + if err != nil { + return "", err + } + } + } // Write the agent mount config. @@ -2298,6 +2307,27 @@ func (vm *qemu) addUSBDeviceConfig(sb *strings.Builder, bus *qemuBus, usbConfig return nil } +func (vm *qemu) addTPMDeviceConfig(sb *strings.Builder, tpmConfig []deviceConfig.RunConfigItem) error { + var socketPath string + + for _, tpmItem := range tpmConfig { + if tpmItem.Key == "path" { + socketPath = tpmItem.Value + } + } + + tplFields := map[string]interface{}{ + "path": socketPath, + } + + err := qemuTMP.Execute(sb, tplFields) + if err != nil { + return err + } + + return nil +} + // pidFilePath returns the path where the qemu process should write its PID. func (vm *qemu) pidFilePath() string { return filepath.Join(vm.LogPath(), "qemu.pid") diff --git a/lxd/instance/drivers/driver_qemu_templates.go b/lxd/instance/drivers/driver_qemu_templates.go index 29d4aa6b40..408aae14d8 100644 --- a/lxd/instance/drivers/driver_qemu_templates.go +++ b/lxd/instance/drivers/driver_qemu_templates.go @@ -540,3 +540,17 @@ driver = "usb-host" bus = "qemu_usb.0" hostdevice = "{{.hostDevice}}" `)) + +var qemuTMP = template.Must(template.New("qemuTMP").Parse(` +[chardev "qemu_tpm-chardev_{{.devName}}"] +backend = "socket" +path = "{{.path}}" + +[tpmdev "qemu_tpm-tpmdev_{{.devName}}"] +type = "emulator" +chardev = "qemu_tpm-chardev" + +[device "dev-lxd_{{.devName}}"] +driver = "tpm-tis" +tpmdev = "qemu_tpm-tpmdev" +`)) From lxc-bot at linuxcontainers.org Fri Oct 16 10:18:31 2020 From: lxc-bot at linuxcontainers.org (brauner on Github) Date: Fri, 16 Oct 2020 03:18:31 -0700 (PDT) Subject: [lxc-devel] [lxd/master] seccomp: fix mount emulation Message-ID: <5f897377.1c69fb81.c6b32.ec51SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 364 bytes Desc: not available URL: -------------- next part -------------- From b76ffcd374cf5577892fff39a13fa56e33ee117f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 16 Oct 2020 10:21:55 +0200 Subject: [PATCH 1/3] seccomp: switch back to pread() process_vm_readv() can read memory from another task if it goes away. Signed-off-by: Christian Brauner --- lxd/seccomp/seccomp.go | 112 ++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 63 deletions(-) diff --git a/lxd/seccomp/seccomp.go b/lxd/seccomp/seccomp.go index 7a4553dee2..3959fab4b4 100644 --- a/lxd/seccomp/seccomp.go +++ b/lxd/seccomp/seccomp.go @@ -1620,66 +1620,67 @@ func (s *Server) HandleMountSyscall(c Instance, siov *Iovec) int { defer pidFd.Close() } - buf1 := [4096]C.char{} - buf2 := [4096]C.char{} - buf3 := [4096]C.char{} - buf4 := [4096]C.char{} - - // process_vm_readv() doesn't like crossing page boundaries when - // reading individual syscall args. - bufSize := 4096 - if bufSize > pageSize { - bufSize = pageSize - } - - mntSource := buf1[:bufSize] - mntTarget := buf2[:bufSize] - mntFs := buf3[:bufSize] - mntData := buf4[:bufSize] - - localIov := []unix.Iovec{ - {Base: (*byte)(unsafe.Pointer(&mntSource[0]))}, - {Base: (*byte)(unsafe.Pointer(&mntTarget[0]))}, - {Base: (*byte)(unsafe.Pointer(&mntFs[0]))}, - {Base: (*byte)(unsafe.Pointer(&mntData[0]))}, - } - - remoteIov := []unix.RemoteIovec{ - {Base: uintptr(siov.req.data.args[0])}, - {Base: uintptr(siov.req.data.args[1])}, - {Base: uintptr(siov.req.data.args[2])}, - {Base: uintptr(siov.req.data.args[4])}, - } + mntSource := [unix.PathMax]C.char{} + mntTarget := [unix.PathMax]C.char{} + mntFs := [unix.PathMax]C.char{} + mntData := [unix.PathMax]C.char{} + // const char *source if siov.req.data.args[0] != 0 { - localIov[0].SetLen(bufSize) - remoteIov[0].Len = bufSize + _, err := C.pread(C.int(siov.memFd), unsafe.Pointer(&mntSource[0]), C.size_t(unix.PathMax), C.off_t(siov.req.data.args[0])) + if err != nil { + ctx["err"] = fmt.Sprintf("Failed to read source path for of mount syscall: %s", err) + ctx["syscall_continue"] = "true" + C.seccomp_notify_update_response(siov.resp, 0, C.uint32_t(seccompUserNotifFlagContinue)) + return 0 + } } + args.source = C.GoString(&mntSource[0]) + ctx["source"] = args.source + // const char *target if siov.req.data.args[1] != 0 { - localIov[1].SetLen(bufSize) - remoteIov[1].Len = bufSize + _, err := C.pread(C.int(siov.memFd), unsafe.Pointer(&mntTarget[0]), C.size_t(unix.PathMax), C.off_t(siov.req.data.args[1])) + if err != nil { + ctx["err"] = fmt.Sprintf("Failed to read target path for of mount syscall: %s", err) + ctx["syscall_continue"] = "true" + C.seccomp_notify_update_response(siov.resp, 0, C.uint32_t(seccompUserNotifFlagContinue)) + return 0 + } } + args.target = C.GoString(&mntTarget[0]) + ctx["target"] = args.target - if siov.req.data.args[2] != 0 { - localIov[2].SetLen(bufSize) - remoteIov[2].Len = bufSize + // const char *filesystemtype + if siov.req.data.args[1] != 0 { + _, err := C.pread(C.int(siov.memFd), unsafe.Pointer(&mntFs[0]), C.size_t(unix.PathMax), C.off_t(siov.req.data.args[2])) + if err != nil { + ctx["err"] = fmt.Sprintf("Failed to read fstype for of mount syscall: %s", err) + ctx["syscall_continue"] = "true" + C.seccomp_notify_update_response(siov.resp, 0, C.uint32_t(seccompUserNotifFlagContinue)) + return 0 + } } + args.fstype = C.GoString(&mntFs[0]) + ctx["fstype"] = args.fstype - if siov.req.data.args[4] != 0 { - localIov[3].SetLen(bufSize) - remoteIov[3].Len = bufSize - } + // unsigned long mountflags + args.flags = int(siov.req.data.args[3]) - _, err := unix.ProcessVMReadv(args.pid, localIov, remoteIov, 0) - if err != nil { - ctx["err"] = fmt.Sprintf("Failed to read process memory of mount syscall: %s", err) - ctx["syscall_continue"] = "true" - C.seccomp_notify_update_response(siov.resp, 0, C.uint32_t(seccompUserNotifFlagContinue)) - return 0 + // const void *data + if siov.req.data.args[4] != 0 { + _, err := C.pread(C.int(siov.memFd), unsafe.Pointer(&mntData[0]), C.size_t(unix.PathMax), C.off_t(siov.req.data.args[4])) + if err != nil { + ctx["err"] = fmt.Sprintf("Failed to read mount data for of mount syscall: %s", err) + ctx["syscall_continue"] = "true" + C.seccomp_notify_update_response(siov.resp, 0, C.uint32_t(seccompUserNotifFlagContinue)) + return 0 + } } + args.data = C.GoString(&mntData[0]) + ctx["data"] = args.data - err = shared.PidfdSendSignal(int(pidFd.Fd()), 0, 0) + err := shared.PidfdSendSignal(int(pidFd.Fd()), 0, 0) if err != nil { ctx["err"] = fmt.Sprintf("Failed to send signal to target process for of mount syscall: %s", err) ctx["syscall_continue"] = "true" @@ -1687,21 +1688,6 @@ func (s *Server) HandleMountSyscall(c Instance, siov *Iovec) int { return 0 } - // const char *source - args.source = C.GoString(&mntSource[0]) - ctx["source"] = args.source - // const char *target - args.target = C.GoString(&mntTarget[0]) - ctx["target"] = args.target - // const char *filesystemtype - args.fstype = C.GoString(&mntFs[0]) - ctx["fstype"] = args.fstype - // unsigned long mountflags - args.flags = int(siov.req.data.args[3]) - // const void *data - args.data = C.GoString(&mntData[0]) - ctx["data"] = args.data - ok, fuseBinary := s.MountSyscallValid(c, &args) if !ok { ctx["syscall_continue"] = "true" From 1dd4a0b1705fcf55f581ff0dcfceb5ec24979098 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 16 Oct 2020 10:54:26 +0200 Subject: [PATCH 2/3] nsexec: simplify userns attach Signed-off-by: Christian Brauner --- lxd/main_nsexec.go | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/lxd/main_nsexec.go b/lxd/main_nsexec.go index 3918547b57..3bfe019832 100644 --- a/lxd/main_nsexec.go +++ b/lxd/main_nsexec.go @@ -142,7 +142,7 @@ int preserve_ns(pid_t pid, int ns_fd, const char *ns) // If the two processes are not in the same namespace returns an fd to the // namespace of the second process identified by @pid2. If the two processes are // in the same namespace returns -EINVAL, -1 if an error occurred. -static int in_same_namespace(pid_t pid1, pid_t pid2, int ns_fd_pid2, const char *ns) +static int in_same_namespace(pid_t pid1, int ns_fd_pid2, const char *ns) { __do_close int ns_fd1 = -EBADF, ns_fd2 = -EBADF; int ret = -1; @@ -158,7 +158,7 @@ static int in_same_namespace(pid_t pid1, pid_t pid2, int ns_fd_pid2, const char return -1; } - ns_fd2 = preserve_ns(pid2, ns_fd_pid2, ns); + ns_fd2 = preserve_ns(-ESRCH, ns_fd_pid2, ns); if (ns_fd2 < 0) return -1; @@ -178,12 +178,12 @@ static int in_same_namespace(pid_t pid1, pid_t pid2, int ns_fd_pid2, const char return move_fd(ns_fd2); } -static void __attach_userns(int pid, int ns_fd) +void attach_userns_fd(int ns_fd) { __do_close int userns_fd = -EBADF; int ret; - userns_fd = in_same_namespace(getpid(), pid, ns_fd, "user"); + userns_fd = in_same_namespace(getpid(), ns_fd, "user"); if (userns_fd < 0) { if (userns_fd == -EINVAL) return; @@ -216,16 +216,6 @@ static void __attach_userns(int pid, int ns_fd) } } -void attach_userns(int pid) -{ - return __attach_userns(pid, -EBADF); -} - -void attach_userns_fd(int ns_fd) -{ - return __attach_userns(-1, ns_fd); -} - int pidfd_nsfd(int pidfd, pid_t pid) { __do_close int ns_fd = -EBADF; From d57813b900c4a9ae3b55b22fb87baf18e1780f2b Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 16 Oct 2020 12:17:16 +0200 Subject: [PATCH 3/3] forksyscall: preserve root and cwd fds for shifted mount emulation Otherwise we won't be able to reacquire the root and current working directory of the target task. Signed-off-by: Christian Brauner --- lxd/main_forksyscall.go | 43 +++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/lxd/main_forksyscall.go b/lxd/main_forksyscall.go index a5b9c565da..be2839f2a0 100644 --- a/lxd/main_forksyscall.go +++ b/lxd/main_forksyscall.go @@ -57,7 +57,8 @@ static bool chdirchroot_in_mntns(int cwd_fd, int root_fd) return true; } -static bool acquire_basic_creds(pid_t pid, int pidfd, int ns_fd) +static bool acquire_basic_creds(pid_t pid, int pidfd, int ns_fd, + int *rootfd, int *cwdfd) { __do_close int cwd_fd = -EBADF, root_fd = -EBADF; char buf[256]; @@ -75,7 +76,27 @@ static bool acquire_basic_creds(pid_t pid, int pidfd, int ns_fd) if (!change_namespaces(pidfd, ns_fd, CLONE_NEWNS)) return false; - return chdirchroot_in_mntns(cwd_fd, root_fd); + if (!chdirchroot_in_mntns(cwd_fd, root_fd)) + return false; + + if (rootfd) + *rootfd = move_fd(root_fd); + + if (cwdfd) + *cwdfd = move_fd(cwd_fd); + return true; +} + +static bool reacquire_basic_creds(int pidfd, int ns_fd, + int root_fd, int cwd_fd) +{ + if (!change_namespaces(pidfd, ns_fd, CLONE_NEWNS)) + return false; + + if (!chdirchroot_in_mntns(cwd_fd, root_fd)) + return false; + + return true; } static bool acquire_final_creds(pid_t pid, uid_t uid, gid_t gid, uid_t fsuid, gid_t fsgid) @@ -157,7 +178,7 @@ static void mknod_emulate(void) fsuid = atoi(advance_arg(true)); fsgid = atoi(advance_arg(true)); - if (!acquire_basic_creds(pid, pidfd, ns_fd)) { + if (!acquire_basic_creds(pid, pidfd, ns_fd, NULL, NULL)) { fprintf(stderr, "%d", ENOANO); _exit(EXIT_FAILURE); } @@ -257,7 +278,7 @@ static void setxattr_emulate(void) size = atoi(advance_arg(true)); data = advance_arg(true); - if (!acquire_basic_creds(pid, pidfd, ns_fd)) { + if (!acquire_basic_creds(pid, pidfd, ns_fd, NULL, NULL)) { fprintf(stderr, "%d", ENOANO); _exit(EXIT_FAILURE); } @@ -336,7 +357,8 @@ static int make_tmpfile(char *template, bool dir) static void mount_emulate(void) { - __do_close int mnt_fd = -EBADF, pidfd = -EBADF, ns_fd = -EBADF; + __do_close int mnt_fd = -EBADF, pidfd = -EBADF, ns_fd = -EBADF, + root_fd = -EBADF, cwd_fd = -EBADF; char *source = NULL, *shiftfs = NULL, *target = NULL, *fstype = NULL; bool use_fuse; uid_t nsuid = -1, uid = -1, nsfsuid = -1, fsuid = -1; @@ -384,7 +406,7 @@ static void mount_emulate(void) change_namespaces(pidfd, ns_fd, CLONE_NEWPID); } - if (!acquire_basic_creds(pid, pidfd, ns_fd)) + if (!acquire_basic_creds(pid, pidfd, ns_fd, &root_fd, &cwd_fd)) _exit(EXIT_FAILURE); if (!acquire_final_creds(pid, uid, gid, fsuid, fsgid)) @@ -441,8 +463,13 @@ static void mount_emulate(void) _exit(EXIT_FAILURE); } - attach_userns_fd(ns_fd); - if (!acquire_basic_creds(pid, pidfd, ns_fd)) { + if (!change_namespaces(pidfd, ns_fd, CLONE_NEWUSER)) { + umount2(target, MNT_DETACH); + umount2(target, MNT_DETACH); + _exit(EXIT_FAILURE); + } + + if (!reacquire_basic_creds(pidfd, ns_fd, root_fd, cwd_fd)) { umount2(target, MNT_DETACH); umount2(target, MNT_DETACH); _exit(EXIT_FAILURE); From lxc-bot at linuxcontainers.org Fri Oct 16 10:23:44 2020 From: lxc-bot at linuxcontainers.org (brauner on Github) Date: Fri, 16 Oct 2020 03:23:44 -0700 (PDT) Subject: [lxc-devel] [lxc/master] seccomp: fix compilation on powerpc Message-ID: <5f8974b0.1c69fb81.a66d3.f0efSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 480 bytes Desc: not available URL: -------------- next part -------------- From 50926f4b2c0dc9b26405153b745d6efa5670203f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 16 Oct 2020 12:22:57 +0200 Subject: [PATCH] seccomp: fix compilation on powerpc Link: https://launchpadlibrarian.net/502200189/buildlog_snap_ubuntu_bionic_ppc64el_lxd-latest-edge_BUILDING.txt.gz Signed-off-by: Christian Brauner --- src/lxc/seccomp.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c index 8ee68df52e..03c1b54f01 100644 --- a/src/lxc/seccomp.c +++ b/src/lxc/seccomp.c @@ -1351,9 +1351,11 @@ static void seccomp_notify_default_answer(int fd, struct seccomp_notif *req, resp->flags = 0; if (seccomp_notify_respond(fd, resp)) - SYSERROR("Failed to send default message to seccomp notification with id(%llu)", resp->id); + SYSERROR("Failed to send default message to seccomp notification with id(%llu)", + (long long unsigned int)resp->id); else - TRACE("Sent default response for seccomp notification with id(%llu)", resp->id); + TRACE("Sent default response for seccomp notification with id(%llu)", + (long long unsigned int)resp->id); memset(resp, 0, handler->conf->seccomp.notifier.sizes.seccomp_notif_resp); } #endif @@ -1414,7 +1416,7 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, /* remember the ID in case we receive garbage from the proxy */ resp->id = req_id = req->id; - TRACE("Received seccomp notification with id(%llu)", req_id); + TRACE("Received seccomp notification with id(%llu)", (long long unsigned int)req_id); snprintf(mem_path, sizeof(mem_path), "/proc/%d", req->pid); fd_pid = open(mem_path, O_RDONLY | O_DIRECTORY | O_CLOEXEC); @@ -1439,7 +1441,7 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, ret = seccomp_notify_id_valid(fd, req->id); if (ret < 0) { seccomp_notify_default_answer(fd, req, resp, hdlr); - SYSERROR("Invalid seccomp notify request id(%llu)", req->id); + SYSERROR("Invalid seccomp notify request id(%llu)", (long long unsigned int)req->id); goto out; } @@ -1498,7 +1500,8 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, } if (resp->id != req_id) { - ERROR("Proxy returned response with illegal id(%llu) != id(%llu)", resp->id, req_id); + ERROR("Proxy returned response with illegal id(%llu) != id(%llu)", + (long long unsigned int)resp->id, (long long unsigned int)req_id); resp->id = req_id; seccomp_notify_default_answer(fd, req, resp, hdlr); goto out; @@ -1512,7 +1515,8 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, } if (resp->id != req_id) { - ERROR("Proxy returned response with illegal id(%llu) != id(%llu)", resp->id, req_id); + ERROR("Proxy returned response with illegal id(%llu) != id(%llu)", + (long long unsigned int)resp->id, (long long unsigned int)req_id); resp->id = req_id; } @@ -1520,7 +1524,8 @@ int seccomp_notify_handler(int fd, uint32_t events, void *data, if (ret) SYSERROR("Failed to send seccomp notification"); else - TRACE("Sent response for seccomp notification with id(%llu)", resp->id); + TRACE("Sent response for seccomp notification with id(%llu)", + (long long unsigned int)resp->id); memset(resp, 0, conf->seccomp.notifier.sizes.seccomp_notif_resp); out: From lxc-bot at linuxcontainers.org Fri Oct 16 11:51:11 2020 From: lxc-bot at linuxcontainers.org (TimRots on Github) Date: Fri, 16 Oct 2020 04:51:11 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxc/init.go: remove for-loop in create() Message-ID: <5f89892f.1c69fb81.9950.4d27SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 391 bytes Desc: not available URL: -------------- next part -------------- From 3bbb97ad0a6b5c25735e2394cea8944dcbdf64be Mon Sep 17 00:00:00 2001 From: Tim Rots Date: Fri, 16 Oct 2020 13:48:51 +0200 Subject: [PATCH] lxc/init.go: remove for-loop in create() * remove unneeded for-loop in create() Signed-off-by: Tim Rots --- lxc/init.go | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/lxc/init.go b/lxc/init.go index ab04aa9f72..51f0343d27 100644 --- a/lxc/init.go +++ b/lxc/init.go @@ -84,6 +84,7 @@ func (c *cmdInit) create(conf *config.Config, args []string) (lxd.InstanceServer var stdinData api.InstancePut var devicesMap map[string]map[string]string var configMap map[string]string + var profiles []string // If stdin isn't a terminal, read text from it if !termios.IsTerminal(getStdinFd()) { @@ -91,9 +92,7 @@ func (c *cmdInit) create(conf *config.Config, args []string) (lxd.InstanceServer if err != nil { return nil, "", err } - - err = yaml.Unmarshal(contents, &stdinData) - if err != nil { + if err = yaml.Unmarshal(contents, &stdinData); err != nil { return nil, "", err } } @@ -145,10 +144,7 @@ func (c *cmdInit) create(conf *config.Config, args []string) (lxd.InstanceServer d = d.UseTarget(c.flagTarget) } - profiles := []string{} - for _, p := range c.flagProfile { - profiles = append(profiles, p) - } + profiles = append(profiles, c.flagProfile...) if !c.global.flagQuiet { if name == "" { @@ -292,14 +288,12 @@ func (c *cmdInit) create(conf *config.Config, args []string) (lxd.InstanceServer Quiet: c.global.flagQuiet, } - _, err = op.AddHandler(progress.UpdateOp) - if err != nil { + if _, err = op.AddHandler(progress.UpdateOp); err != nil { progress.Done("") return nil, "", err } - err = utils.CancelableWait(op, &progress) - if err != nil { + if err = utils.CancelableWait(op, &progress); err != nil { progress.Done("") return nil, "", err } @@ -320,8 +314,7 @@ func (c *cmdInit) create(conf *config.Config, args []string) (lxd.InstanceServer return nil, "", err } - err = op.Wait() - if err != nil { + if err = op.Wait(); err != nil { return nil, "", err } From noreply at github.com Fri Oct 16 12:17:46 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Fri, 16 Oct 2020 05:17:46 -0700 Subject: [lxc-devel] [lxc/lxc] 50926f: seccomp: fix compilation on powerpc Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: 50926f4b2c0dc9b26405153b745d6efa5670203f https://github.com/lxc/lxc/commit/50926f4b2c0dc9b26405153b745d6efa5670203f Author: Christian Brauner Date: 2020-10-16 (Fri, 16 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- seccomp: fix compilation on powerpc Link: https://launchpadlibrarian.net/502200189/buildlog_snap_ubuntu_bionic_ppc64el_lxd-latest-edge_BUILDING.txt.gz Signed-off-by: Christian Brauner Commit: a282f7792fcc557a340dc24233d52074b8469f18 https://github.com/lxc/lxc/commit/a282f7792fcc557a340dc24233d52074b8469f18 Author: Stéphane Graber Date: 2020-10-16 (Fri, 16 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- Merge pull request #3555 from brauner/2020-10-16/seccomp seccomp: fix compilation on powerpc Compare: https://github.com/lxc/lxc/compare/eb587451d078...a282f7792fcc From builds at travis-ci.org Fri Oct 16 12:36:43 2020 From: builds at travis-ci.org (Travis CI) Date: Fri, 16 Oct 2020 12:36:43 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7895 (master - a282f77) In-Reply-To: Message-ID: <5f8993db4d93c_13fae8c53bc48931a6@travis-tasks-685697cf47-2pxb9.mail> Build Update for lxc/lxc ------------------------------------- Build: #7895 Status: Errored Duration: 18 mins and 11 secs Commit: a282f77 (master) Author: Stéphane Graber Message: Merge pull request #3555 from brauner/2020-10-16/seccomp seccomp: fix compilation on powerpc View the changeset: https://github.com/lxc/lxc/compare/eb587451d078...a282f7792fcc View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/736345535?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lxc-bot at linuxcontainers.org Fri Oct 16 13:57:55 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Fri, 16 Oct 2020 06:57:55 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: Adds support for external subnets for OVN networks Message-ID: <5f89a6e3.1c69fb81.9a9e7.8bd3SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 1031 bytes Desc: not available URL: -------------- next part -------------- From 05044a7c2567cd52534bc48ac61e207363f56556 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 16 Oct 2020 14:31:22 +0100 Subject: [PATCH 1/6] lxd/network/openvswitch/ovn: Adds LogicalSwitchPortGetDNS to return switch port DNS info Signed-off-by: Thomas Parrott --- lxd/network/openvswitch/ovn.go | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go index bb08c505af..475398d23e 100644 --- a/lxd/network/openvswitch/ovn.go +++ b/lxd/network/openvswitch/ovn.go @@ -791,6 +791,41 @@ func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPo return dnsIPv4, dnsIPv6, nil } +// LogicalSwitchPortGetDNS returns the logical switch port DNS info (UUID, name and IPs). +func (o *OVN) LogicalSwitchPortGetDNS(portName OVNSwitchPort) (string, string, []net.IP, error) { + // Get UUID and DNS IPs for a switch port in the format: ",= " + output, err := o.nbctl("--format=csv", "--no-headings", "--data=bare", "--colum=_uuid,records", "find", "dns", + fmt.Sprintf("external_ids:lxd_switch_port=%s", string(portName)), + ) + if err != nil { + return "", "", nil, err + } + + parts := strings.Split(strings.TrimSpace(output), ",") + dnsUUID := strings.TrimSpace(parts[0]) + + var dnsName string + var ips []net.IP + + // Try and parse the DNS name and IPs. + if len(parts) > 1 { + dnsParts := strings.SplitN(strings.TrimSpace(parts[1]), "=", 2) + if len(dnsParts) == 2 { + dnsName = strings.TrimSpace(dnsParts[0]) + ipParts := strings.Split(dnsParts[1], " ") + for _, ipPart := range ipParts { + ip := net.ParseIP(strings.TrimSpace(ipPart)) + if ip != nil { + ips = append(ips, ip) + } + } + } + + } + + return dnsUUID, dnsName, ips, nil +} + // LogicalSwitchPortDeleteDNS removes DNS records for a switch port. func (o *OVN) LogicalSwitchPortDeleteDNS(switchName OVNSwitch, portName OVNSwitchPort) error { // Check if existing DNS record exists for switch port. From 835968cbbdc2e9f104422e93102cea9f8c063408 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 16 Oct 2020 14:30:47 +0100 Subject: [PATCH 2/6] lxd/network/openvswitch/ovn: Updates LogicalSwitchPortDeleteDNS to only accept DNS UUID rather than port name Signed-off-by: Thomas Parrott --- lxd/network/openvswitch/ovn.go | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go index 475398d23e..b6a05abf18 100644 --- a/lxd/network/openvswitch/ovn.go +++ b/lxd/network/openvswitch/ovn.go @@ -827,28 +827,17 @@ func (o *OVN) LogicalSwitchPortGetDNS(portName OVNSwitchPort) (string, string, [ } // LogicalSwitchPortDeleteDNS removes DNS records for a switch port. -func (o *OVN) LogicalSwitchPortDeleteDNS(switchName OVNSwitch, portName OVNSwitchPort) error { - // Check if existing DNS record exists for switch port. - dnsUUID, err := o.nbctl("--format=csv", "--no-headings", "--data=bare", "--colum=_uuid", "find", "dns", - fmt.Sprintf("external_ids:lxd_switch_port=%s", string(portName)), - ) +func (o *OVN) LogicalSwitchPortDeleteDNS(switchName OVNSwitch, dnsUUID string) error { + // Remove DNS record association from switch. + _, err := o.nbctl("remove", "logical_switch", string(switchName), "dns_records", dnsUUID) if err != nil { return err } - dnsUUID = strings.TrimSpace(dnsUUID) - if dnsUUID != "" { - // Remove DNS record association from switch. - _, err = o.nbctl("remove", "logical_switch", string(switchName), "dns_records", dnsUUID) - if err != nil { - return err - } - - // Remove DNS record entry itself. - _, err = o.nbctl("destroy", "dns", dnsUUID) - if err != nil { - return err - } + // Remove DNS record entry itself. + _, err = o.nbctl("destroy", "dns", dnsUUID) + if err != nil { + return err } return nil From cf1670c565cb62f9d22348fe81078fcc55fcdf22 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 16 Oct 2020 14:28:49 +0100 Subject: [PATCH 3/6] lxd/network/openvswitch/ovn: Updates LogicalSwitchPortSetDNS to return the DNS UUID record ID Uses for for reverting a record created when LogicalSwitchPortSetDNS is updated to only accept DNS UUID. Signed-off-by: Thomas Parrott --- lxd/network/openvswitch/ovn.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go index b6a05abf18..a52cc5e595 100644 --- a/lxd/network/openvswitch/ovn.go +++ b/lxd/network/openvswitch/ovn.go @@ -681,8 +681,8 @@ func (o *OVN) LogicalSwitchPortDynamicIPs(portName OVNSwitchPort) ([]net.IP, err // LogicalSwitchPortSetDNS sets up the switch DNS records for the DNS name resolving to the IPs of the switch port. // Attempts to find at most one IP for each IP protocol, preferring static addresses over dynamic. -// Returns the IPv4 and IPv6 addresses used for DNS records. -func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPort, dnsName string) (net.IP, net.IP, error) { +// Returns the DNS record UUID, IPv4 and IPv6 addresses used for DNS records. +func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPort, dnsName string) (string, net.IP, net.IP, error) { var dnsIPv4, dnsIPv6 net.IP // checkAndStoreIP checks if the supplied IP is valid and can be used for a missing DNS IP variable. @@ -705,7 +705,7 @@ func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPo // Get static and dynamic IPs for switch port. staticAddressesRaw, err := o.nbctl("lsp-get-addresses", string(portName)) if err != nil { - return nil, nil, err + return "", nil, nil, err } staticAddresses := strings.Split(strings.TrimSpace(staticAddressesRaw), " ") @@ -729,7 +729,7 @@ func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPo if hasDynamic && (dnsIPv4 == nil || dnsIPv6 == nil) { dynamicIPs, err := o.LogicalSwitchPortDynamicIPs(portName) if err != nil { - return nil, nil, err + return "", nil, nil, err } for _, dynamicIP := range dynamicIPs { @@ -757,7 +757,7 @@ func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPo fmt.Sprintf("external_ids:lxd_switch_port=%s", string(portName)), ) if err != nil { - return nil, nil, err + return "", nil, nil, err } cmdArgs := []string{ @@ -771,13 +771,13 @@ func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPo // Update existing record if exists. _, err = o.nbctl(append([]string{"set", "dns", dnsUUID}, cmdArgs...)...) if err != nil { - return nil, nil, err + return "", nil, nil, err } } else { // Create new record if needed. dnsUUID, err = o.nbctl(append([]string{"create", "dns"}, cmdArgs...)...) if err != nil { - return nil, nil, err + return "", nil, nil, err } dnsUUID = strings.TrimSpace(dnsUUID) } @@ -785,10 +785,10 @@ func (o *OVN) LogicalSwitchPortSetDNS(switchName OVNSwitch, portName OVNSwitchPo // Add DNS record to switch DNS records. _, err = o.nbctl("add", "logical_switch", string(switchName), "dns_records", dnsUUID) if err != nil { - return nil, nil, err + return "", nil, nil, err } - return dnsIPv4, dnsIPv6, nil + return dnsUUID, dnsIPv4, dnsIPv6, nil } // LogicalSwitchPortGetDNS returns the logical switch port DNS info (UUID, name and IPs). From 631c1e64f0dd47ac61aaa93b655da9b00f3fc17a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 16 Oct 2020 14:32:48 +0100 Subject: [PATCH 4/6] lxd/network/driver/ovn: Generates static EUI64 IPv6 address for instance switch ports in instanceDevicePortAdd When only static IPv4 addresses have been added to a logical switch port. This ensures that the switch port has an IPv6 address, as OVN has a limitation that prevents a port from being statically addressed for IPv4 and dynamically allocated for IPv6. This in turn meant that if using the `ipv4.address` key without an associated `ipv6.address` key, then AAAA DNS record would not be created. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index a6dc816a7a..47f1c0daf9 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1873,6 +1873,30 @@ func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, deviceN if err != nil { return "", err } + + // If port isn't going to have fully dynamic IPs allocated by OVN, and instead only static IPv4 + // addresses have been added, then add an EUI64 static IPv6 address so that the switch port has an + // IPv6 address that will be used to generate a DNS record. This works around a limitation in OVN + // that prevents us requesting dynamic IPv6 address allocation when static IPv4 allocation is used. + if len(ips) > 0 { + hasIPv6 := false + for _, ip := range ips { + if ip.To4() == nil { + hasIPv6 = true + break + } + } + + if !hasIPv6 { + eui64IP, err := eui64.ParseMAC(routerIntPortIPv6Net.IP, mac) + if err != nil { + return "", errors.Wrapf(err, "Failed generating EUI64 for instance port %q", mac.String()) + } + + // Add EUI64 to list of static IPs for instance port. + ips = append(ips, eui64IP) + } + } } instancePortName := n.getInstanceDevicePortName(instanceID, deviceName) From 512fc6bad083c6ba698e8f28b15f44b624bb9ee1 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 16 Oct 2020 14:27:44 +0100 Subject: [PATCH 5/6] lxd/network/driver/ovn: Adds support for publishing instance port IPs to uplink network Uses the IPs in the DNS record for a switch port for publishing. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 63 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 47f1c0daf9..9c41271966 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1922,12 +1922,50 @@ func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, deviceN return "", err } - dnsIPv4, dnsIPv6, err := client.LogicalSwitchPortSetDNS(n.getIntSwitchName(), instancePortName, fmt.Sprintf("%s.%s", instanceName, n.getDomainName())) + dnsUUID, dnsIPv4, dnsIPv6, err := client.LogicalSwitchPortSetDNS(n.getIntSwitchName(), instancePortName, fmt.Sprintf("%s.%s", instanceName, n.getDomainName())) if err != nil { return "", err } - revert.Add(func() { client.LogicalSwitchPortDeleteDNS(n.getIntSwitchName(), instancePortName) }) + revert.Add(func() { client.LogicalSwitchPortDeleteDNS(n.getIntSwitchName(), dnsUUID) }) + + // Parse the network's external routes so we can check if the port's IPs fall within them and should be + // published to the uplink network. + for _, k := range []string{"ipv4.routes.external", "ipv6.routes.external"} { + if n.config[k] == "" { + continue + } + + var ip net.IP + + // Select the correct destination IP and check that NAT is disabled on the network. + if k == "ipv4.routes.external" && !shared.IsTrue(n.config["ipv4.nat"]) { + ip = dnsIPv4 + } else if k == "ipv6.routes.external" && !shared.IsTrue(n.config["ipv6.nat"]) { + ip = dnsIPv6 + } + + if ip == nil { + continue //No qualifying target IP to check. + } + + netExternalRoutes, err := SubnetParseAppend([]*net.IPNet{}, strings.Split(n.config[k], ",")...) + if err != nil { + return "", err + } + + for _, netExternalRoute := range netExternalRoutes { + if netExternalRoute.Contains(ip) { + err = client.LogicalRouterDNATSNATAdd(n.getRouterName(), ip, ip, true, true) + if err != nil { + return "", err + } + + revert.Add(func() { client.LogicalRouterDNATSNATDelete(n.getRouterName(), ip) }) + break // Confirmed IP should be published on uplink, no need to look further. + } + } + } // Add each internal route (using the IPs set for DNS as target). for _, internalRoute := range internalRoutes { @@ -2015,11 +2053,30 @@ func (n *ovn) instanceDevicePortDelete(instanceID int, deviceName string, intern return err } - err = client.LogicalSwitchPortDeleteDNS(n.getIntSwitchName(), instancePortName) + // Delete DNS records. + dnsUUID, _, dnsIPs, err := client.LogicalSwitchPortGetDNS(instancePortName) + if err != nil { + return err + } + + err = client.LogicalSwitchPortDeleteDNS(n.getIntSwitchName(), dnsUUID) if err != nil { return err } + // Delete any associated external IP DNAT rules for the DNS IPs (if NAT disabled). + for _, dnsIP := range dnsIPs { + isV6 := dnsIP.To4() == nil + + // Atempt to remove any externally published IP rules if the associated IP NAT setting is disabled. + if (!isV6 && !shared.IsTrue(n.config["ipv4.nat"])) || (isV6 && !shared.IsTrue(n.config["ipv6.nat"])) { + err = client.LogicalRouterDNATSNATDelete(n.getRouterName(), dnsIP) + if err != nil { + return err + } + } + } + // Delete each internal route. for _, internalRoute := range internalRoutes { err = client.LogicalRouterRouteDelete(n.getRouterName(), internalRoute, nil) From 13613e869eb58f30225b93285bbc62f8f71814d7 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 16 Oct 2020 10:18:27 +0100 Subject: [PATCH 6/6] lxd/device/nic/ovn: Improved error messages Signed-off-by: Thomas Parrott --- lxd/device/nic_ovn.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go index 766adbcdf0..091888ab60 100644 --- a/lxd/device/nic_ovn.go +++ b/lxd/device/nic_ovn.go @@ -300,7 +300,7 @@ func (d *nicOVN) Start() (*deviceConfig.RunConfig, error) { internalRoutes, err = network.SubnetParseAppend(internalRoutes, strings.Split(d.config[key], ",")...) if err != nil { - return nil, errors.Wrapf(err, "Invalid %s", key) + return nil, errors.Wrapf(err, "Invalid %q value", key) } } @@ -312,7 +312,7 @@ func (d *nicOVN) Start() (*deviceConfig.RunConfig, error) { externalRoutes, err = network.SubnetParseAppend(externalRoutes, strings.Split(d.config[key], ",")...) if err != nil { - return nil, errors.Wrapf(err, "Invalid %s", key) + return nil, errors.Wrapf(err, "Invalid %q value", key) } } @@ -439,7 +439,7 @@ func (d *nicOVN) Stop() (*deviceConfig.RunConfig, error) { internalRoutes, err = network.SubnetParseAppend(internalRoutes, strings.Split(d.config[key], ",")...) if err != nil { - return nil, errors.Wrapf(err, "Invalid %s", key) + return nil, errors.Wrapf(err, "Invalid %q value", key) } } @@ -451,7 +451,7 @@ func (d *nicOVN) Stop() (*deviceConfig.RunConfig, error) { externalRoutes, err = network.SubnetParseAppend(externalRoutes, strings.Split(d.config[key], ",")...) if err != nil { - return nil, errors.Wrapf(err, "Invalid %s", key) + return nil, errors.Wrapf(err, "Invalid %q value", key) } } From lxc-bot at linuxcontainers.org Fri Oct 16 14:29:24 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Fri, 16 Oct 2020 07:29:24 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: Generates EUI64 IPv6 DNS record for OVN NICs when static IPv4 address is defined Message-ID: <5f89ae44.1c69fb81.dbfad.7157SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 719 bytes Desc: not available URL: -------------- next part -------------- From c470380fd10673ee00dd9bf1e28371bf4e3fb2bb Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 16 Oct 2020 10:18:27 +0100 Subject: [PATCH 1/2] lxd/device/nic/ovn: Improved error messages Signed-off-by: Thomas Parrott --- lxd/device/nic_ovn.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go index 766adbcdf0..091888ab60 100644 --- a/lxd/device/nic_ovn.go +++ b/lxd/device/nic_ovn.go @@ -300,7 +300,7 @@ func (d *nicOVN) Start() (*deviceConfig.RunConfig, error) { internalRoutes, err = network.SubnetParseAppend(internalRoutes, strings.Split(d.config[key], ",")...) if err != nil { - return nil, errors.Wrapf(err, "Invalid %s", key) + return nil, errors.Wrapf(err, "Invalid %q value", key) } } @@ -312,7 +312,7 @@ func (d *nicOVN) Start() (*deviceConfig.RunConfig, error) { externalRoutes, err = network.SubnetParseAppend(externalRoutes, strings.Split(d.config[key], ",")...) if err != nil { - return nil, errors.Wrapf(err, "Invalid %s", key) + return nil, errors.Wrapf(err, "Invalid %q value", key) } } @@ -439,7 +439,7 @@ func (d *nicOVN) Stop() (*deviceConfig.RunConfig, error) { internalRoutes, err = network.SubnetParseAppend(internalRoutes, strings.Split(d.config[key], ",")...) if err != nil { - return nil, errors.Wrapf(err, "Invalid %s", key) + return nil, errors.Wrapf(err, "Invalid %q value", key) } } @@ -451,7 +451,7 @@ func (d *nicOVN) Stop() (*deviceConfig.RunConfig, error) { externalRoutes, err = network.SubnetParseAppend(externalRoutes, strings.Split(d.config[key], ",")...) if err != nil { - return nil, errors.Wrapf(err, "Invalid %s", key) + return nil, errors.Wrapf(err, "Invalid %q value", key) } } From 1666e7465339f83f126960324849268e495a4c71 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 16 Oct 2020 14:32:48 +0100 Subject: [PATCH 2/2] lxd/network/driver/ovn: Generates static EUI64 IPv6 address for instance switch ports in instanceDevicePortAdd When only static IPv4 addresses have been added to a logical switch port. This ensures that the switch port has an IPv6 address, as OVN has a limitation that prevents a port from being statically addressed for IPv4 and dynamically allocated for IPv6. This in turn meant that if using the `ipv4.address` key without an associated `ipv6.address` key, then AAAA DNS record would not be created. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index a6dc816a7a..47f1c0daf9 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1873,6 +1873,30 @@ func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, deviceN if err != nil { return "", err } + + // If port isn't going to have fully dynamic IPs allocated by OVN, and instead only static IPv4 + // addresses have been added, then add an EUI64 static IPv6 address so that the switch port has an + // IPv6 address that will be used to generate a DNS record. This works around a limitation in OVN + // that prevents us requesting dynamic IPv6 address allocation when static IPv4 allocation is used. + if len(ips) > 0 { + hasIPv6 := false + for _, ip := range ips { + if ip.To4() == nil { + hasIPv6 = true + break + } + } + + if !hasIPv6 { + eui64IP, err := eui64.ParseMAC(routerIntPortIPv6Net.IP, mac) + if err != nil { + return "", errors.Wrapf(err, "Failed generating EUI64 for instance port %q", mac.String()) + } + + // Add EUI64 to list of static IPs for instance port. + ips = append(ips, eui64IP) + } + } } instancePortName := n.getInstanceDevicePortName(instanceID, deviceName) From lxc-bot at linuxcontainers.org Fri Oct 16 17:14:12 2020 From: lxc-bot at linuxcontainers.org (TimRots on Github) Date: Fri, 16 Oct 2020 10:14:12 -0700 (PDT) Subject: [lxc-devel] [lxd/master] revert/revert.go: remove a for-loop from Clone() method Message-ID: <5f89d4e4.1c69fb81.d47db.7851SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 398 bytes Desc: not available URL: -------------- next part -------------- From 22b08974776a5d2135215bdd66286e7ccb42ba56 Mon Sep 17 00:00:00 2001 From: Tim Rots Date: Fri, 16 Oct 2020 19:08:35 +0200 Subject: [PATCH] revert/revert.go: remove a for-loop from Clone() Use a single append to concatenate two slices Signed-off-by: Tim Rots --- lxd/revert/revert.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lxd/revert/revert.go b/lxd/revert/revert.go index dbe65b7edf..4b8642a55d 100644 --- a/lxd/revert/revert.go +++ b/lxd/revert/revert.go @@ -38,10 +38,7 @@ func (r *Reverter) Success() { func (r *Reverter) Clone() *Reverter { rNew := New() rNew.revertFuncs = make([]func(), 0, len(r.revertFuncs)) - - for _, f := range r.revertFuncs { - rNew.revertFuncs = append(rNew.revertFuncs, f) - } + rNew.revertFuncs = append(rNew.revertFuncs, r.revertFuncs...) return rNew } From lxc-bot at linuxcontainers.org Sat Oct 17 22:18:14 2020 From: lxc-bot at linuxcontainers.org (TimRots on Github) Date: Sat, 17 Oct 2020 15:18:14 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxc/copy.go: Remove unneeded for-loop in c.Run() Message-ID: <5f8b6da6.1c69fb81.b5020.94d6SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 395 bytes Desc: not available URL: -------------- next part -------------- From 4d4fb034f61025b41845e191073ada05bf5ac856 Mon Sep 17 00:00:00 2001 From: Tim Rots Date: Sun, 18 Oct 2020 00:16:43 +0200 Subject: [PATCH] lxc/copy.go: Remove unneeded for-loop in c.Run() * Removed unneeded forloop in lxc/copy.go Signed-off-by: Tim Rots --- lxc/image.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lxc/image.go b/lxc/image.go index 424173691d..1d1900174f 100644 --- a/lxc/image.go +++ b/lxc/image.go @@ -254,9 +254,7 @@ func (c *cmdImageCopy) Run(cmd *cobra.Command, args []string) error { if c.flagCopyAliases { // Also add the original aliases - for _, alias := range imgInfo.Aliases { - aliases = append(aliases, alias) - } + aliases = append(aliases, imgInfo.Aliases...) } err = ensureImageAliases(destinationServer, aliases, fp) From lxc-bot at linuxcontainers.org Mon Oct 19 02:02:23 2020 From: lxc-bot at linuxcontainers.org (lxc-jp on Github) Date: Sun, 18 Oct 2020 19:02:23 -0700 (PDT) Subject: [lxc-devel] [linuxcontainers.org/master] Add Japanese release announcement of LXD 4.7 Message-ID: <5f8cf3af.1c69fb81.6f644.2696SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 370 bytes Desc: not available URL: -------------- next part -------------- From 9de5e289b67d9b1c7a37fc69c144d5c20ed45373 Mon Sep 17 00:00:00 2001 From: KATOH Yasufumi Date: Sun, 18 Oct 2020 17:33:28 +0900 Subject: [PATCH] Add Japanese release announcement of LXD 4.7 Signed-off-by: KATOH Yasufumi --- content/lxd/news.ja/lxd-4.7.yaml | 571 +++++++++++++++++++++++++++++++ 1 file changed, 571 insertions(+) create mode 100644 content/lxd/news.ja/lxd-4.7.yaml diff --git a/content/lxd/news.ja/lxd-4.7.yaml b/content/lxd/news.ja/lxd-4.7.yaml new file mode 100644 index 0000000..6322553 --- /dev/null +++ b/content/lxd/news.ja/lxd-4.7.yaml @@ -0,0 +1,571 @@ +title: LXD 4.7 リリースのお知らせ +date: 2020/10/16 17:10 +origin: https://discuss.linuxcontainers.org/t/lxd-4-7-has-been-released/9213 +content: |- + ### はじめに + + LXD チームは LXD 4.7 のリリースをお知らせできることにとてもワクワクしています! + + + このリリースには、VM でとても歓迎すべきいくつかの改良(USB と Live メモリのアップデート)、バックアップ機能の充実、OVN 仮想ネットワークを使う場合の多数の改良が含まれています。 + + Enjoy! + + ### 新機能とハイライト + #### カスタムストレージボリュームのバックアップ(export/import) + + 新たにカスタムボリュームに対する backup API が追加されました。これにより、cli で `lxc storage volume export` と `lxc storage volume import` が使えるようになりました。 + + stgraber at castiana:~$ lxc storage volume create default foo + Storage volume foo created + stgraber at castiana:~$ lxc storage volume export default foo + Backup exported successfully! + stgraber at castiana:~$ lxc storage volume delete default foo + Storage volume foo deleted + stgraber at castiana:~$ lxc storage volume import default backup.tar.gz + stgraber at castiana:~$ lxc storage volume list default + +----------------------------+------------------------------------------------------------------+-------------+--------------+---------+ + | TYPE | NAME | DESCRIPTION | CONTENT TYPE | USED BY | + +----------------------------+------------------------------------------------------------------+-------------+--------------+---------+ + | container | lxd-build | | filesystem | 1 | + +----------------------------+------------------------------------------------------------------+-------------+--------------+---------+ + | container | lxd-build-focal | | filesystem | 1 | + +----------------------------+------------------------------------------------------------------+-------------+--------------+---------+ + | container | steam | | filesystem | 1 | + +----------------------------+------------------------------------------------------------------+-------------+--------------+---------+ + | custom | backups | | filesystem | 1 | + +----------------------------+------------------------------------------------------------------+-------------+--------------+---------+ + | custom | foo | | filesystem | 0 | + +----------------------------+------------------------------------------------------------------+-------------+--------------+---------+ + | custom | images | | filesystem | 1 | + +----------------------------+------------------------------------------------------------------+-------------+--------------+---------+ + + + #### 別の名前でのインスタンスのインポート + + ついに別の名前でインスタンスのバックアップをインポートできるようになりました! + + stgraber at castiana:~$ lxc init images:alpine/edge a1 + Creating a1 + stgraber at castiana:~$ lxc export a1 + Backup exported successfully! + stgraber at castiana:~$ lxc import backup.tar.gz a2 + stgraber at castiana:~$ lxc list a + +------+---------+------+------+-----------+-----------+ + | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | + +------+---------+------+------+-----------+-----------+ + | a1 | STOPPED | | | CONTAINER | 0 | + +------+---------+------+------+-----------+-----------+ + | a2 | STOPPED | | | CONTAINER | 0 | + +------+---------+------+------+-----------+-----------+ + + #### 仮想マシンのメモリの圧縮(と再増加) + + 仮想マシン内のバルーンデバイスを制御できるようになりました。メモリ容量を縮小させ、その後で再度前の制限にまで戻すことができるようになりました(さらなるメモリの追加にはリブートが必要です)。 + + stgraber at castiana:~$ lxc config show ubuntu-desktop | grep memory + limits.memory: 2GiB + stgraber at castiana:~$ lxc exec ubuntu-desktop -- free -m + total used free shared buff/cache available + Mem: 1983 437 822 7 722 1386 + Swap: 448 0 448 + stgraber at castiana:~$ lxc config set ubuntu-desktop limits.memory 1500MiB + stgraber at castiana:~$ lxc exec ubuntu-desktop -- free -m + total used free shared buff/cache available + Mem: 1435 436 276 7 722 840 + Swap: 448 0 448 + stgraber at castiana:~$ lxc config set ubuntu-desktop limits.memory 2GiB + stgraber at castiana:~$ lxc exec ubuntu-desktop -- free -m + total used free shared buff/cache available + Mem: 1983 437 822 7 722 1387 + Swap: 448 0 448 + stgraber at castiana:~$ + + #### 仮想マシンに対する USB デバイスのパススルー + + `usb` デバイスタイプが仮想マシンで使えるようになりました。 + 新しいデバイスを追加するには再起動が必要な点をのぞいては、コンテナと全く同じように動作します。 + + + さらに、3 つの仮想ポートが LXD の VM に接続されており、`lxc console --type=vga` を使って、リモートの USB デバイスへのリダイレクションが使えます。 + + #### マイグレーション時の rsync 圧縮が設定可能に + + 新たに真偽値の `rsync.compression` オプションがストレージプールに対して追加されました。 + + + これにより、ネットワークが充分に高速でボトルネックにならず、圧縮が原因で CPU 使用率が問題となるような場合に、マイグレーション操作中の rsync 圧縮を無効にできるようになりました。 + + #### プロジェクトのネットワークで使えるアップリンクの制限 + + OVN ネットワーク機能を有効にしたプロジェクトを使っている場合、仮想マシンが使うアップリンクネットワークを制限できるようになりました。 + + + アップリンクネットワークがひとつだけの場合、ユーザーが指定しなくても LXD は自動的にそのネットワークを使います。 + + + これはプロジェクトに新たに追加された `restricted.networks.uplinks` オプションで行います。 + + #### 新たに追加された管理 `physical` ネットワークタイプ + + 新たに `physical` ネットワークタイプが追加されました。これは、現時点では OVN ネットワークに対するアップリンクとしてのみ使えます。設定には OVN ネットワークで使える IP アドレスのセットと、ゲートウェイ、DNS サーバが含まれています。 + + stgraber at castiana:~$ lxc network create external parent=eth0 ipv4.gateway=172.17.0.1/24 ipv4.ovn.ranges=172.17.0.100-172.17.0.150 dns.nameservers=1.1.1.1 --type=physical + Network external created + stgraber at castiana:~$ lxc network list + +----------+----------+---------+----------------+---------------------------+-------------+---------+ + | NAME | TYPE | MANAGED | IPV4 | IPV6 | DESCRIPTION | USED BY | + +----------+----------+---------+----------------+---------------------------+-------------+---------+ + | eth1 | physical | NO | | | | 0 | + +----------+----------+---------+----------------+---------------------------+-------------+---------+ + | external | physical | YES | | | | 0 | + +----------+----------+---------+----------------+---------------------------+-------------+---------+ + | lxdbr0 | bridge | YES | 10.166.11.1/24 | fd42:4c81:5770:1eaf::1/64 | | 15 | + +----------+----------+---------+----------------+---------------------------+-------------+---------+ + | virbr0 | bridge | NO | | | | 0 | + +----------+----------+---------+----------------+---------------------------+-------------+---------+ + | wlan0 | physical | NO | | | | 0 | + +----------+----------+---------+----------------+---------------------------+-------------+---------+ + + #### OVN ネットワークでの外部ルーティングアドレス・サブネットのサポート + + 新たな設定キー `ipv4.routes.external` と `ipv6.routes.external` を使って、外部の IP アドレスとサブネットを OVN ネットワーク上で実行中のインスタンスにルーティングできるようになりました。 + + + プロジェクト設定の新たな `restricted.networks.subnets` キーと一緒に使用して、まずは外部の IPv4/IPv6 サブネットのセットを特定のプロジェクトに委任し、そのプロジェクト内でそれらのアドレスをインスタンスにルーティングできます。 + + ### すべての変更点(翻訳なし) + + このリリースでの完全な変更点のリストは次のとおりです: + + - lxd/cluster: Changing "no heartbeat" language in membership.go "no heartbeat since " changed to "no heartbeat for " + - lxd/apparmor: Allow unix sockets binding + - doc/server: Fix escaping + - doc/server: Sort config keys + - lxd/backup: Adds WorkingDirPrefix constant + - lxd: backup.WorkingDirPrefix usage + - lxd/backup: Updates backupCreate to store backups in backups/instances + - lxd/backup: Updates Rename to support new backup location + - lxd/backup: Rename comment ending + - lxd/backup: Updates DoBackupDelete to handle new backup location + - lxd/backup: DoBackupDelete comment ending + - lxd/instance/backup: Updates containerBackupExportGet to support new backup location + - lxd/patches: Adds patchMoveBackupsInstances to move backups into backups/instances dir + - lxd/sys/fs: Adds backups/custom and backups/instances to initDirs() + - lxd/network/driver/ovn: Improve error message when parent 'network' option not specified + - lxd/network/network/interface: Adds Type interface and moves non-DB depedent functions into it + - lxd/network/network/load: Adds LoadByType function and removes ValidateNameAndProject function + - lxd/main/init/interactive: netType.ValidateName usage + - lxd/networks: Switch to network type validation in networksPost + - lxd/networks: Use ValidateName function on loaded DB network in networkPost + - lxd/network/network/interface: Exports FillConfig + - lxd/network/network/load: Removes FillConfig function + - lxd/networks: netType.FillConfig usage + - lxd/network/driver/common: Exports FillConfig + - lxd/network/driver/bridge: FillConfig usage + - lxd/network/driver/ovn: FillConfig usage + - lxd/network/driver/common: Removes common Type() and netType + - lxd/network: Adds Type() to each driver + - lxd/db/errors: Updates ErrAlreadyDefined text to be generic + - lxd/network/network/interface: Adds DBType function + - lxd/network/driver: Implements DBType() + - lxd/network/driver: Adds NodeSpecificConfig Info var + - lxd/networks: netType.DBType usage in networksPost + - lxd/networks: Create pending network node entries when network driver doesn't support per node config in networksPost + - lxd/networks: Comments in networksPostCluster + - lxd/networks: Comments in networkGet + - lxd/networks: Start parent networks before dependents in networkStartup + - lxd: Ensure all use of db.InstanceFilter defines instance type + - lxd/project/permissions: Fixes AllowInstanceCreation tests + - lxd/project/permissions: Error quoting + - api: Add projects_networks + - doc/storage: no need to escape underscore in bash examples + - seccomp: fix bpf support detection + - seccomp: improve bpf support detection + - shared/validate: Use ParseUint in IsNetworkMTU + - lxd/device/device/utils/network: Change argument for NetworkSetDevMTU to uint32 + - lxd/device/device/utils/network: NetworkSetDevMTU usage + - lxd/network/network/utils: Changes GetDevMTU to return uint32 + - lxd/network/openvswitch/ovs: Adds OVNEncapIP function + - lxd/network/driver/ovn: Removes ovnGeneveTunnelMTU constant + - lxd/network/network/utils/ovn: Removes OVNInstanceDeviceMTU function + - lxd/network/driver/ovn: Updates getBridgeMTU() to not depend on ovnGeneveTunnelMTU + - lxd/network/driver/ovn: Adds getOptimalBridgeMTU and getUnderlayInfo functions + - lxd/network/driver/ovn: Updates setup to generate an optimal bridge.mtu setting if not specified manually + - lxd/device/nic/ovn: Read mtu directly from parent network config bridge.mtu setting + - doc/projects: Sort config keys + - lxd/networks: Enforces manage-networks RBAC permission for managing networks + - lxd/network: Only adding pseudo pending node records when in cluster in networksPost + - lxd/project/permissions: Typo + - lxd/db/cluster/open: Adds features.networks to default project on new database + - lxd/storage/cephfs: Fix quota on new volumes + - lxd/networks: Allow network deletion in projects + - lxc/remote: Add project selection logic + - i18n: Update translation templates + - lxd/network: Removes client side default network type when creating network + - lxd/networks: Default to ovn network type when creating non-default network project + - lxd/network: Removes client side default network type when creating network + - lxd/networks: Default to ovn network type when creating non-default network project + - api: Adds projects_networks_restricted_uplinks extension + - doc/projects: Adds restricted.networks.uplinks + - lxd/networks: Updates doNetworkUpdate to use n.Validate so that project is available to validator + - lxd/network/network/load: Removes unused Validate + - lxd/network/network/load: Renames project arg to projectName for clarity + - lxd/api/project: Adds restricted.networks.uplinks to validation + - lxd/network/driver/ovn: Adds allowedUplinkNetworks function + - lxd/network/driver/ovn: Enforce project restricted.networks.uplinks setting + - lxd/instances: Fix ceph cluster target move + - lxd/cgroup: Fix memory.swappiness detection + - lxd/db: Adds boolean support to doDbQueryScan + - lxd/sys/fs: initDirs comment + - lxd/sys/fs: Removes backups/instances and backups/custom from pre-storage mount setup + - lxd/sys/fs: initDirs error quoting + - lxd/sys/fs: Adds initStorageDirs to be called after storage pools and daemon volumes are mounted + - lxd/sys/os: Adds InitStorage + - lxd/daemon: Call d.os.InitStorage after daemon storage volumes are mounted + - lxd/backup/instance/config: Renames InstanceConfig to Config + - lxd/backup/backup/config: Makes Config fields omitempty so custom volume's encoded yaml doesn't contain instance fields + - lxd/backup/backup/config: Adds comment to Container field explaining that VM backups use this too + - lxd/storage/pool/interface: backup.Config usage + - lxd/api/internal: backup.ParseConfigYamlFile usage + - lxd/storage/backend: backup.Config usage + - lxd/backup: Moves Instance interface into own file + - lxd/backup: Moves Info struct and GetInfo function into own file + - lxd/backup: Renames backup to backup_common + - lxd/backup/backup/common: Renames Backup to BackupCommon + - lxd/backup/backup/instance: Adds InstanceBackup using CommonBackup as basis + - lxd/backup: Changes pruneExpiredContainerBackups to use InstanceBackup.Delete() function + - lxd/instance/instance/utils: backup.InstanceBackup usage + - lxd/instance/instance/interface: backup.InstanceBackup usage + - lxd/instance/drivers: backup.InstanceBackup usage + - lxd/rbac: Avoid tight retry loop + - lxd/rbac: Directly handle re-tries on 504 + - lxd/backup/backup/utils: Adds TarReader function + - lxd/backup/backup/info: Changes Type field from api.InstanceType to Type + - lxd/backup/backup/info: Updates GetInfo to use TarReader + - lxc/backup: Updates backupWriteIndex to use backup.Type + - lxd/backup/backup/info: GetInfo consistent comment endings + - lxd/backup/backup/info: Updates GetInfo to support backup.Type + - lxd/db/backups: InstanceBackup comment + - lxd/db/backups: projectName argument renaming + - lxd/db/storage/volumes: Set Snapshot: true in StorageVolumeArgs returned from GetLocalStoragePoolVolumeSnapshotsWithType + - lxd/instance: Spacing + - lxd/storage/drivers/driver/btrfs/utils: Switches to backup.TarReader + - lxd/storage/drivers/driver/btrfs: Consistent comment ending + - lxd/storage/drivers/driver/zfs/volumes: consistent comment ending + - lxd/storage/drivers/generic/vfs: Consistent comment ending + - lxd/backup/backup/info: Adds note about legacy container.bin optimized type check + - lxd/backup/backup/instance: Fix old parent directory removal in InstanceBackup.Rename() + - lxd/backup/backup/config: Adds VolumeSnapshots to Config struct + - lxd/backup/backup/info: Adds Config field to Info struct + - lxd/backup/backup/info: Adds TypeCustom backup type for custom volumes + - lxd/backup/backup/volume: Adds custom volume type + - lxd/storage/volumes/backup: Adds custom volume backup route handlers + - client/interfaces: Add custom volume backup functions + - client/interfaces: Adds StoragePoolVolumeBackupArgs struct + - client/lxd/storage/volumes: Add custom volume backup functions + - api: Adds custom_volume_backup extension + - doc/rest-api: Documents custom volume backup routes + - lxc/storage/volumes: Add import and export for custom volumes + - lxd/backup: Adds volumeBackupCreate and volumeBackupWriteIndex functions + - lxd/api/1/0: Registers custom volume backup route handlers + - lxd/db/backups: Adds StoragePoolVolumeBackup type + - lxd/db/backups: Adds custom volume backup lifecycle functions + - lxd/db/cluster: Adds storage_volumes_backups table + - lxd/db/operations/types: Adds custom volume backup operations types + - lxd/db/storage/volume/snapshots: Adds GetStorageVolumeSnapshotsNames function + - lxd/storage/backend: Adds BackupCustomVolume and CreateCustomVolumeFromBackup functions + - lxd/storage/backend/lxd: Renames custom volume backups in RenameCustomVolume + - lxd/storage/backend/lxd: Deletes custom volume backups in DeleteCustomVolume + - lxd/storage/drivers/driver/btrfs/volumes: Adds support for optimized custom volume backups + - lxd/storage/drivers/driver/dir/volumes: Adds support for custom volume backups nil post hooks + - lxd/storage/drivers/driver/zfs/volumes: Adds support for optimized custom volume backups + - lxd/storage/drivers/driver/zfs/volumes: Adds support for custom volume backups nil post hooks + - lxd/storage/drivers/generic/vfs: Adds support for custom volume backups to genericVFSBackupVolume + - lxd/storage/drivers/generic/vfs: Adds support for custom volume backups to genericVFSBackupUnpack + - lxd/storage/pool/interface: Adds BackupCustomVolume and CreateCustomVolumeFromBackup + - lxd/storage/volume: Adds createStoragePoolVolumeFromBackup and hook in storagePoolVolumesTypePost + - lxd/storage/volumes/utils: Adds storagePoolVolumeBackupLoadByName function + - shared/api/storage/pool/volume: Adds custom volume backup structs + - test/suites/backup: Adds tests for custom volume backups + - i18n: Update translation template + - i18n: Update translations from weblate + - lxc: Always use HostPathFollow + - lxd/storage/drivers/generic/vfs: Fixes custom volume root dir ownership issue in genericVFSBackupUnpack + - test/suites/backup: Use project argument in test_backup_import_with_project + - test/suites/backup: Use project argument in test_backup_export_with_project + - test/suites/backup: Use project argument in test_backup_volume_export_with_project + - test/suites/backup: Adds test for backup import into different project in test_backup_import_with_project + - test/suites/backup: Comment consistency + - test/suites/backup: Add test for custom volume import + - test/suites/backup: Add test for importing custom volume into other project + - lxd/api: Restrict access to daemon config + - lxd/storage: Allow ceph/cephfs for images/backups + - client/interfaces: Adds Name field to InstanceBackupArgs + - client/lxd/instances: Adds custom name restore support to CreateInstanceFromBackup + - lxd/instance/drivers/qmp/monitor: Adds GetBalloonSizeBytes and SetBalloonSizeBytes + - lxd/instance/drivers/driver/qemu: Adds live shrinking of memory + - lxd/devices/config/devices/utils: Adds doc block for deviceEquals and deviceEqualsDiffKeys + - lxd/device/config/devices: Comment clean up + - lxd/device/config/devices: Improves comments and variable naming in Update + - lxd/device/config/devices: Fixes bug in Update where allChangedKeys only contains changed keys from last device + - lxd/instance/drivers/driver/lxc: Whitespace + - lxd/device/config/devices: Handles nil updateFields function in Update + - lxd/instance/drivers/driver/qemu: Removes logic duplication in live update + - lxc/import: Adds optional instance name argument to lxc import command + - lxd/instances/post: Adds custom name support for backup import to createFromBackup + - lxd/instances/post: createFromBackup usage in containersPost for custom backup name restore + - lxd/api/internal: Adds AllowNameOverride to internalImportPost + - lxd/api/internal: Override instance name in internalImport when AllowNameOverride is set + - client/interfaces: Adds Name field to StoragePoolVolumeBackupArgs to bring in line with InstanceBackupArgs + - client/lxd/storage/volumes: Updates CreateStoragePoolVolumeFromBackup to accept volume name override via X-LXD-name header + - lxc/storage/volume: Adds optional volume name argument to lxc storage volume import + - lxd/storage/volumes: Adds volName arg to createStoragePoolVolumeFromBackup + - lxd/storage/volumes: createStoragePoolVolumeFromBackup usage in storagePoolVolumesTypePost + - lxd/storage/backend/lxd: Updates CreateCustomVolumeFromBackup to support custom volume import name + - api: Adds backup_override_name extension + - test/suites/backup: Adds tests for custom volume import name override + - test/suites/backup: Adds instance import name override tests + - i18n: Update translation template + - doc/networks: Simplifies OVN single node setup instructions + - lxd/device/nic/ovn: Improves error message in Start + - lxd/network/driver/ovn: Implements DHCPv4Subnet and DHCPv6Subnet to allow static IPs to be set + - lxd/network/openvswitch/ovn: Fix spelling of OVNIPv6AddressModeDHCPStateful and OVNIPv6AddressModeDHCPStateless values + - lxd/network/driver/ovn: Adds support for ipv6.dhcp.stateful + - doc/networks: Documents ipv6.dhcp.stateful option for OVN networks + - shared/api: Not all disks have a device path + - lxd/resources: Ignore rbd devices + - shared/simplestreams: Fix stream's index download url + - lxd/device/device/interface: Adds NICState interface for getting NIC state + - lxd/device/nic/bridged: Implements NICState interface by adding State function + - lxd/instance/drivers/driver/qemu: Refactors RenderState to support multiple NIC types in the future + - lxd/network/openvswitch/ovn: Adds LogicalSwitchPortDynamicIPs function + - lxd/network/openvswitch/ovn: Updates LogicalSwitchPortSetDNS to use LogicalSwitchPortDynamicIPs + - lxd/network/driver/ovn: Adds instanceDevicePortDynamicIPs function + - lxd/network/network/utils/ovn: Adds OVNInstanceDevicePortDynamicIPs function + - lxd/device/nic/ovn: Implements NICState interface by adding State function + - lxd/instance/drivers/qmp/monitor: Renames GetMemoryBalloonSizeBytes + - lxd/instance/drivers/qmp/monitor: Renames SetMemoryBalloonSizeBytes + - lxd/instance/drivers/qmp/monitor: Adds GetMemorySizeBytes function + - lxd/instance/drivers/driver/qemu: Adds qemuDefaultMemSize constant + - lxd/instance/drivers/driver/qemu: Updates updateMemoryLimit to allow memory resize back to boot time size + - lxd/instance/drivers/driver/qemu: Updates IsRunning to not check for BROKEN state + - lxd/instance/drivers/driver/qemu: Updates statusCode() to detect if monitor failure with running VM + - lxd/apparmor: Allow access to zoneinfo files + - lxd/apparmor: Add /etc/localtime to the list + - lxd/project: Always allow cloud-init:config drives + - doc/image-handling: Cover publishing + - lxd/network/network/utils: Adds GetNeighbourIPs function + - lxd/network/network/utils: Updates GetLeaseAddresses to return only net.IP list + - lxd/device/nic/bridged: Updates State() to return partial data + - refuse empty passwords + - lxd/storage: Adds rsync.compression config key + - doc: Adds rsync.compression + - api: storage_rsync_compression + - tests: Valid rsync.compression + - doc/index: Add libsqlite3-dev back to dependencies + - lxd/firewall/drivers/driver/nftables: Updates nft parser to handle nft sets with composite `type` field + - shared/validate/validate: Increases max MTU to 16384 to support super jumbo packets + - lxd/apparmor/forkproxy: Fix bad profile name + - lxd/apparmor/forkproxy: Allow writing to log path + - lxc: Better handle copy/move between projects + - lxd/apparmor: Fix version parsing + - lxd/dnsmasq: Switch to Parse for version parsing + - lxd/firewall/drivers: Fix to Parse for version parsing + - lxd/rsync: Switch to Parse for version parsing + - shared/version: Make patch optional + - lxd/networks: Log error in doNetworksCreate after failed create if cleanup fails too + - lxd/network/driver: Improve missing parent network error message + - lxd/network/driver/ovn: Moves uplink type agnostic parent port allocation logic into allocateParentPortIPs() + - lxd/network/driver/ovn: Better error messages + - lxd/network/driver/ovn: Moves parent port lock into deleteParentPort + - lxd/network/driver/ovn: Moves parent port lock into startParentPort + - lxd/network/driver/ovn: deleteParentPortBridge comments + - lxd/network/driver/ovn: Don't setup SNAT if no external uplink IPs + - lxd/network/driver/ovn: Makes setting up external router port and switch conditional on having external IPs + - lxd/network/driver/ovn: Removes old comment + - lxd/network/driver/ovn: Fix sentence in startParentPortBridge error + - lxd/network/driver/ovn: Fixes error message in setupParentPortBridge + - lxd/network/network/utils: Moves bridge related functions into own file + - static_analysis: exclude vendored headers from spell checking + - static_analysis: exclude .git + - shift_linux: vendor posix_acl_xattr.h + - seccomp: vendor bpf headers + - shares/validate: Whitespace + - lxd/network/openvswitch/ovn: Updates RecursiveDNSServer to be list of IPs + - lxd/network/driver/ovn: Updates allocateParentPortIPs to detect the parent network IP address and DNS settings + - lxd/network/driver/ovn: Updates n.allocateParentPortIPs usage + - lxd/network/driver/ovn: Updates setup IPv6 RDNSS setting + - lxd/apparmor/forkproxy: Socket path fixes + - lxd/images: Fix crash when no "info" struct + - doc/networks: Clarifies use of ovn ranges settings in bridge network + - lxd/util/net: Updates SysctlSet to support setting multiple keys + - shared/validate: Adds IsNetworkAddressList function + - lxd/network/network/utils: Adds VLANInterfaceCreate function + - lxd/device/device/utils/network: network.VLANInterfaceCreate usage + - lxd/device/device/utils/network: Removes NetworkRemoveInterface function + - lxd/network/network/utils: Adds InterfaceRemove and InterfaceExists functions + - lxd/network/network/utils: InterfaceExists usage + - lxd/device/device/utils/network: network.InterfaceRemove usage + - lxd/device/nic: network.InterfaceRemove usage + - lxd/network/driver/bridge: InterfaceExists usage + - lxd/network/driver/ovn: InterfaceExists usage + - lxd/network/network/utils: Adds InterfaceSetMTU function + - lxd/device: network.InterfaceSetMTU usage + - lxd/network/driver/ovn: Inherit MTU from uplink bridge for OVS bridge and connecting veth pair + - lxd/network/driver/ovn: Remove dependency on sysctl command and use util.SysctlSet instead + - lxd/network/driver: Improves comments + - api: Adds network_type_physical extension + - doc/networks: Adds docs for physical network type + - lxd/db/networks: Adds physical network type constant + - lxd/network/driver/physical: Adds physical driver + - lxd/network/driver/ovn: Adds support for physical network as uplink + - lxd/network/driver/physical: Change checkParentUse to return a bool if in use + - lxd/network/driver/ovn: Changes uplink network in use check to look at LXD DB + - lxd/network/driver/ovn: Handle uplink network changing + - lxd/network/driver/ovn: Comment clarity + - lxd/storage/pools: Gives clear error message when trying to create duplicate storage pool in single node + - lxd/events: Validate type + - lxd/events: Prevent logging access to non-admin + - lxd/daemon: Clean shutdown on SIGPWR/SIGTERM + - lxd/operations: Don't directly trigger shutdown + - lxd: Prevent internal cluster migration of instances with backups + - lxd/instance/drivers: Enable USB for VMs + - lxd/instance/drivers: Add USB controller to QEMU config + - lxd/apparmor: Fix devPaths in QEMU profile + - db: Retry transient errors for longer + - db: Always retry driver.ErrBusy, regardless of the error message + - db: Retry failed rollbacks if they are due to transient errors + - db: Explicitly rollback leftover transactions when a new one can't be started + - db: Retry to begin a new transaction after an explicit rollback attempt + - lxd/operations: Fix timeout + - lxd/daemon: Allow more operations during shutdown + - lxd/include: Relocate ifndef for NEWCGROUP + - doc: Remove stray \_ escapes in security.md + - lxc-to-lxd: Handle snap better + - lxd/device/usb: Allow USB devices for VMs + - lxd/device: Add bus and dev number to USBEvent + - lxd/apparmor: Allow USB specific paths + - lxd/device/config: Add USBDevice to RunConfig + - lxd/events: Handle default permissiosn in projects + - lxd/dnsmasq: Adds 100ms sleep to successful Kill() to allow sockets to be released by OS + - lxd/instance/drivers/driver/qemu: Restores ability to resize VM disks + - lxd/device/disk: Adds comment about VM instances depending on CanHotPlug fields for stopped disk resize + - lxd/instance/qemu: Fix bad event name + - lxd/storage: Check base image is available locally + - lxd/storage/drivers/driver/lvm: Don't remove empty thinpool and volume group if lvm.vg.force_reuse enabled + - shared/validate/validate: Removes inaccurate comments about optional values + - shared/validate/validate: Adds IsNetwork and IsNetworkList functions + - shared/validate/validate: Re-orders IP validation functions + - lxd/device/nic/ovn: Comment + - doc/api-extensions: Removes mention of "parent" from projects_networks_restricted_uplinks feature + - doc/networks: Switch to "uplink" terminology for external OVN network access + - lxd/network/driver/ovn: Replace parent terminology with uplink + - lxd/network/driver/common: Ban : char from network names in ValidateName() + - lxd/device: Handle USB devices for VMs + - lxd/instance/drivers: Add qemuUSBDev template + - lxd/instance/drivers: Add USB devices to qemu config + - Revert "lxd/instance/drivers: Enable USB for VMs" + - lxd/driver/qemu: Add spice usb ports + - lxd-agent: Fix defer in for loop + - forksyscall: use correct function + - shared/util.go: use string method with stdout and stderr + - simplestreams.go: remove unneeded fmt.Sprintf and simplify getImages() + - lxd/instance/drivers: Updates templateApplyNow to close files at end of each iteration + - lxd/network/network/utils: Adds SubnetContains function + - lxd/network/network/utils: Adds SubnetIterate function + - lxd/network/network/utils: Adds SubnetParseAppend function + - api: Adds network_ovn_external_subnets extension + - doc/networks: Adds ipv4.routes and ipv6.routes settings to physical network + - lxd/network/driver/physical: Adds ipv4.routes and ipv6.routes config keys + - doc/projects: Removes trailing full stop + - doc/projects: Adds restricted.networks.subnets + - lxd/api/project: Adds restricted.networks.subnets config key + - lxd/api/project: Moves projectConfigKeys inside projectValidateConfig and adds state + - lxd/api/project: projectValidateConfig usage + - lxd/api/project: Adds projectValidateRestrictedSubnets function + - lxd/api/project: Adds restricted.networks.subnets validation to projectValidateConfig + - doc/networks: Adds ipv4.routes.external and ipv6.routes.external to ovn networks + - lxd/network/openvswitch/ovn: Adds LogicalRouterRouteDelete function + - lxd/network/openvswitch/ovn: Updates LogicalSwitchPortSetDNS to return IPs used for DNS records + - lxd/network/openvswitch/ovn: Adds LogicalRouterDNATSNATAdd function + - lxd/network/openvswitch/ovn: Adds LogicalRouterDNATSNATDelete function + - lxd/network/openvswitch/ovn: Updates LogicalRouterRouteAdd to support mayExist argument + - lxd/network/network/utils/ovn: Updates OVNInstanceDevicePortAdd to take an externalRoutes argument + - lxd/network/network/utils/ovn: Updates OVNInstanceDevicePortDelete to accept an externalRoutes argument + - lxd/network/driver/ovn: Moves uplink network validation into validateUplinkNetwork function + - lxd/network/driver/ovn: Updates Validate to check network exists and checks external IP routes + - lxd/network/driver/ovn: Adds DNS revert to instanceDevicePortAdd + - lxd/network/driver/ovn: client.LogicalRouterRouteAdd usage + - lxd/network/driver/ovn: Adds externalRoutes support to instanceDevicePortAdd + - lxd/network/driver/ovn: Delete externalRoutes in instanceDevicePortDelete + - forkmount: improve + - seccomp: improve logging for the seccomp notifier + - seccomp: make sure that insertMountLXD() doesn't call into LXC + - lxd/device/nic: Adds ipv4.routes.external and ipv6.routes.external to nicValidationRules + - lxd/device/nic/ovn: Adds support for ipv4.routes.external and ipv6.routes.external + - doc/instances: Adds ovn NIC documentation + - doc/instances: Re-works NIC device docs to explain nictype and network fields + - lxd/network/driver/ovn: Adds support for OVN NIC internal routes + - lxd/network/network/utils/ovn: Adds OVN NIC internal route support to OVNInstanceDevicePortAdd and OVNInstanceDevicePortDelete + - lxd/device/nic/ovn: Adds ipv4.routes and ipv6.routes settings for internal route support + - lxd/network/driver/bridge: Fixes inconsistency between normal bridge and fan bridge default ipv4.nat value + - api: Adds network_ovn_nat extension + - doc/networks: Adds ipv4.nat and ipv6.nat to OVN networks + - lxd/network/driver/ovn: Adds ipv4.nat and ipv6.nat support + - lxd/patches: Adds patchNetworkOVNEnableNAT patch to enable NAT on OVN networks + - i18n: Update translations from weblate + + ### 試用環境 + + この新しい LXD リリースは私たちの [デモサービス](https://linuxcontainers.org/ja/lxd/try-it/) で利用できます。 + + ### ダウンロード + + このリリースの tarball は [ダウンロードページ](/lxd/downloads/) から取得できます。 + + + ビルド済みバイナリーは次のように使えます: + + - **Linux:** snap install lxd + - **MacOS:** brew install lxc + - **Windows:** choco install lxc From lxc-bot at linuxcontainers.org Mon Oct 19 02:50:50 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Sun, 18 Oct 2020 19:50:50 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/db/networks: Fix NULL description Message-ID: <5f8cff0a.1c69fb81.55fa8.2504SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 370 bytes Desc: not available URL: -------------- next part -------------- From 9ba0b5a029cbf659553ff5be801afe542a97e645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Sun, 18 Oct 2020 22:50:26 -0400 Subject: [PATCH] lxd/db/networks: Fix NULL description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #8051 Signed-off-by: Stéphane Graber --- lxd/db/networks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/db/networks.go b/lxd/db/networks.go index 3b10447bde..b2549cabd5 100644 --- a/lxd/db/networks.go +++ b/lxd/db/networks.go @@ -72,7 +72,7 @@ func (c *ClusterTx) GetNonPendingNetworkIDs() (map[string]int64, error) { // // Pending networks are skipped. func (c *ClusterTx) GetNonPendingNetworks() (map[string]map[int64]api.Network, error) { - stmt, err := c.tx.Prepare(`SELECT projects.name, networks.id, networks.name, networks.description, networks.type, networks.state + stmt, err := c.tx.Prepare(`SELECT projects.name, networks.id, networks.name, coalesce(networks.description, ''), networks.type, networks.state FROM networks JOIN projects on projects.id = networks.project_id WHERE networks.state != ? From lxc-bot at linuxcontainers.org Mon Oct 19 10:22:48 2020 From: lxc-bot at linuxcontainers.org (brauner on Github) Date: Mon, 19 Oct 2020 03:22:48 -0700 (PDT) Subject: [lxc-devel] [lxc/master] startup fixes Message-ID: <5f8d68f8.1c69fb81.69a98.0f19SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 364 bytes Desc: not available URL: -------------- next part -------------- From 35f0c46e0da931d32c297d203831ea5da9bef72c Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 19 Oct 2020 11:46:08 +0200 Subject: [PATCH 1/3] sync: switch to new error helpers Signed-off-by: Christian Brauner --- src/lxc/sync.c | 55 ++++++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/src/lxc/sync.c b/src/lxc/sync.c index c98357cb64..52c065f539 100644 --- a/src/lxc/sync.c +++ b/src/lxc/sync.c @@ -23,30 +23,21 @@ static int __sync_wait(int fd, int sequence) ssize_t ret; ret = lxc_read_nointr(fd, &sync, sizeof(sync)); - if (ret < 0) { - SYSERROR("Sync wait failure"); - return -1; - } + if (ret < 0) + return log_error_errno(-1, errno, "Sync wait failure"); if (!ret) return 0; - if ((size_t)ret != sizeof(sync)) { - ERROR("Unexpected sync size: %zu expected %zu", (size_t)ret, sizeof(sync)); - return -1; - } + if ((size_t)ret != sizeof(sync)) + return log_error(-1, "Unexpected sync size: %zu expected %zu", (size_t)ret, sizeof(sync)); - if (sync == LXC_SYNC_ERROR) { - ERROR("An error occurred in another process " - "(expected sequence number %d)", sequence); - return -1; - } + if (sync == LXC_SYNC_ERROR) + return log_error(-1, "An error occurred in another process (expected sequence number %d)", sequence); + + if (sync != sequence) + return log_error(-1, "Invalid sequence number %d. Expected sequence number %d", sync, sequence); - if (sync != sequence) { - ERROR("Invalid sequence number %d. Expected sequence number %d", - sync, sequence); - return -1; - } return 0; } @@ -54,10 +45,9 @@ static int __sync_wake(int fd, int sequence) { int sync = sequence; - if (lxc_write_nointr(fd, &sync, sizeof(sync)) < 0) { - SYSERROR("Sync wake failure"); - return -1; - } + if (lxc_write_nointr(fd, &sync, sizeof(sync)) < 0) + return log_error_errno(-1, errno, "Sync wake failure"); + return 0; } @@ -65,6 +55,7 @@ static int __sync_barrier(int fd, int sequence) { if (__sync_wake(fd, sequence)) return -1; + return __sync_wait(fd, sequence+1); } @@ -103,31 +94,25 @@ int lxc_sync_init(struct lxc_handler *handler) int ret; ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, handler->sync_sock); - if (ret) { - SYSERROR("failed to create synchronization socketpair"); - return -1; - } + if (ret) + return log_error_errno(-1, errno, "failed to create synchronization socketpair"); /* Be sure we don't inherit this after the exec */ - fcntl(handler->sync_sock[0], F_SETFD, FD_CLOEXEC); + ret = fcntl(handler->sync_sock[0], F_SETFD, FD_CLOEXEC); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to make socket close-on-exec"); return 0; } void lxc_sync_fini_child(struct lxc_handler *handler) { - if (handler->sync_sock[0] != -1) { - close(handler->sync_sock[0]); - handler->sync_sock[0] = -1; - } + close_prot_errno_disarm(handler->sync_sock[0]); } void lxc_sync_fini_parent(struct lxc_handler *handler) { - if (handler->sync_sock[1] != -1) { - close(handler->sync_sock[1]); - handler->sync_sock[1] = -1; - } + close_prot_errno_disarm(handler->sync_sock[1]); } void lxc_sync_fini(struct lxc_handler *handler) From 5befd767a6cb65e3c08456c73501b7fec63b564c Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 19 Oct 2020 11:56:53 +0200 Subject: [PATCH 2/3] sync: log synchronization states Signed-off-by: Christian Brauner --- src/lxc/sync.c | 11 ++++++++++- src/lxc/sync.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/lxc/sync.c b/src/lxc/sync.c index 52c065f539..7ccdb71fc5 100644 --- a/src/lxc/sync.c +++ b/src/lxc/sync.c @@ -56,36 +56,44 @@ static int __sync_barrier(int fd, int sequence) if (__sync_wake(fd, sequence)) return -1; - return __sync_wait(fd, sequence+1); + return __sync_wait(fd, sequence + 1); } int lxc_sync_barrier_parent(struct lxc_handler *handler, int sequence) { + TRACE("Child waking parent with sequence %s and waiting for sequence %s", + sync_to_string(sequence), sync_to_string(sequence + 1)); return __sync_barrier(handler->sync_sock[0], sequence); } int lxc_sync_barrier_child(struct lxc_handler *handler, int sequence) { + TRACE("Parent waking child with sequence %s and waiting with sequence %s", + sync_to_string(sequence), sync_to_string(sequence + 1)); return __sync_barrier(handler->sync_sock[1], sequence); } int lxc_sync_wake_parent(struct lxc_handler *handler, int sequence) { + TRACE("Child waking parent with sequence %s", sync_to_string(sequence)); return __sync_wake(handler->sync_sock[0], sequence); } int lxc_sync_wait_parent(struct lxc_handler *handler, int sequence) { + TRACE("Parent waiting for child with sequence %s", sync_to_string(sequence)); return __sync_wait(handler->sync_sock[0], sequence); } int lxc_sync_wait_child(struct lxc_handler *handler, int sequence) { + TRACE("Child waiting for parent with sequence %s", sync_to_string(sequence)); return __sync_wait(handler->sync_sock[1], sequence); } int lxc_sync_wake_child(struct lxc_handler *handler, int sequence) { + TRACE("Child waking parent with sequence %s", sync_to_string(sequence)); return __sync_wake(handler->sync_sock[1], sequence); } @@ -102,6 +110,7 @@ int lxc_sync_init(struct lxc_handler *handler) if (ret < 0) return log_error_errno(-1, errno, "Failed to make socket close-on-exec"); + TRACE("Initialized synchronization infrastructure"); return 0; } diff --git a/src/lxc/sync.h b/src/lxc/sync.h index c09ac8fb05..944853f631 100644 --- a/src/lxc/sync.h +++ b/src/lxc/sync.h @@ -20,6 +20,34 @@ enum { LXC_SYNC_ERROR = -1 /* Used to report errors from another process */ }; +static inline const char *sync_to_string(int state) +{ + switch (state) { + case LXC_SYNC_STARTUP: + return "startup"; + case LXC_SYNC_CONFIGURE: + return "configure"; + case LXC_SYNC_POST_CONFIGURE: + return "post-configure"; + case LXC_SYNC_CGROUP: + return "cgroup"; + case LXC_SYNC_CGROUP_UNSHARE: + return "cgroup-unshare"; + case LXC_SYNC_CGROUP_LIMITS: + return "cgroup-limits"; + case LXC_SYNC_READY_START: + return "ready-start"; + case LXC_SYNC_RESTART: + return "restart"; + case LXC_SYNC_POST_RESTART: + return "post-restart"; + case LXC_SYNC_ERROR: + return "error"; + default: + return "invalid sync state"; + } +} + __hidden extern int lxc_sync_init(struct lxc_handler *handler); __hidden extern void lxc_sync_fini(struct lxc_handler *); __hidden extern void lxc_sync_fini_parent(struct lxc_handler *); From fbfe5c8208fd8304ee74b2e297585c64a0d6bd81 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 19 Oct 2020 11:38:17 +0200 Subject: [PATCH 3/3] start: improve devpts fd sending Closes: #3549. Signed-off-by: Christian Brauner --- src/lxc/conf.c | 4 +++- src/lxc/start.c | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 5962009e34..b8058ffdce 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1531,13 +1531,15 @@ static int lxc_setup_devpts(struct lxc_handler *handler) devpts_fd = openat(-EBADF, "/dev/pts", O_CLOEXEC | O_DIRECTORY | O_PATH | O_NOFOLLOW); if (devpts_fd < 0) { + devpts_fd = -EBADF; TRACE("Failed to create detached devpts mount"); - ret = lxc_abstract_unix_send_fds(sock, NULL, 0, NULL, 0); + ret = lxc_abstract_unix_send_fds(sock, NULL, 0, &devpts_fd, sizeof(int)); } else { ret = lxc_abstract_unix_send_fds(sock, &devpts_fd, 1, NULL, 0); } if (ret < 0) return log_error_errno(-1, errno, "Failed to send devpts fd to parent"); + TRACE("Sent devpts file descriptor %d to parent", devpts_fd); /* Remove any pre-existing /dev/ptmx file. */ ret = remove("/dev/ptmx"); diff --git a/src/lxc/start.c b/src/lxc/start.c index 322debf00f..7b29d40834 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -1953,13 +1953,14 @@ static int lxc_spawn(struct lxc_handler *handler) } } - ret = lxc_abstract_unix_recv_fds(data_sock1, &handler->conf->devpts_fd, 1, NULL, 0); + ret = lxc_abstract_unix_recv_fds(data_sock1, &handler->conf->devpts_fd, 1, + &handler->conf->devpts_fd, + sizeof(handler->conf->devpts_fd)); if (ret < 0) { SYSERROR("Failed to receive devpts fd from child"); goto out_delete_net; } - if (ret == 0) - handler->conf->devpts_fd = -EBADF; + TRACE("Received devpts file descriptor %d from child", handler->conf->devpts_fd); /* Now all networks are created, network devices are moved into place, * and the correct names and ifindices in the respective namespaces have From lxc-bot at linuxcontainers.org Mon Oct 19 10:26:15 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Mon, 19 Oct 2020 03:26:15 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: Adds support for "none" in "ipv4.address" and "ipv6.address" settings Message-ID: <5f8d69c7.1c69fb81.de2e5.4e28SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 0e5c2730002452dbc53a11c3dab6e681cd19d3df Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 09:29:59 +0100 Subject: [PATCH 1/4] lxd/network/driver/ovn: Allows "none" as value for ipv4.address and ipv6.address Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 5ea70ba071..04b846b3f1 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -120,14 +120,14 @@ func (n *ovn) Validate(config map[string]string) error { "bridge.hwaddr": validate.Optional(validate.IsNetworkMAC), "bridge.mtu": validate.Optional(validate.IsNetworkMTU), "ipv4.address": func(value string) error { - if validate.IsOneOf(value, []string{"auto"}) == nil { + if validate.IsOneOf(value, []string{"none", "auto"}) == nil { return nil } return validate.Optional(validate.IsNetworkAddressCIDRV4)(value) }, "ipv6.address": func(value string) error { - if validate.IsOneOf(value, []string{"auto"}) == nil { + if validate.IsOneOf(value, []string{"none", "auto"}) == nil { return nil } @@ -219,7 +219,7 @@ func (n *ovn) Validate(config map[string]string) error { // If NAT disabled, check subnets are within the uplink network's routes and project's subnet restrictions. for _, keyPrefix := range []string{"ipv4", "ipv6"} { - if !shared.IsTrue(config[fmt.Sprintf("%s.nat", keyPrefix)]) && config[fmt.Sprintf("%s.address", keyPrefix)] != "" { + if !shared.IsTrue(config[fmt.Sprintf("%s.nat", keyPrefix)]) && validate.IsOneOf(config[fmt.Sprintf("%s.address", keyPrefix)], []string{"", "none", "auto"}) != nil { _, ipNet, err := net.ParseCIDR(config[fmt.Sprintf("%s.address", keyPrefix)]) if err != nil { return err From 9d4ceec468a1fc0d1a81597cddba19793f97951b Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 11:18:44 +0100 Subject: [PATCH 2/4] lxd/network/driver/ovn: Re-run validation of auto generated address used in FillConfig Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 04b846b3f1..d8b9b39714 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1122,14 +1122,18 @@ func (n *ovn) deleteUplinkPortPhysical(uplinkNet Network) error { // FillConfig fills requested config with any default values. func (n *ovn) FillConfig(config map[string]string) error { + changedConfig := false + if config["ipv4.address"] == "" { config["ipv4.address"] = "auto" + changedConfig = true } if config["ipv6.address"] == "" { content, err := ioutil.ReadFile("/proc/sys/net/ipv6/conf/default/disable_ipv6") if err == nil && string(content) == "0\n" { config["ipv6.address"] = "auto" + changedConfig = true } } @@ -1145,6 +1149,8 @@ func (n *ovn) FillConfig(config map[string]string) error { if config["ipv4.nat"] == "" { config["ipv4.nat"] = "true" } + + changedConfig = true } if config["ipv6.address"] == "auto" { @@ -1158,6 +1164,12 @@ func (n *ovn) FillConfig(config map[string]string) error { if config["ipv6.nat"] == "" { config["ipv6.nat"] = "true" } + + changedConfig = true + } + + if changedConfig { + return n.Validate(config) } return nil From f3da8b57c47423616e6490c89fb820be6c5dd651 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 11:21:00 +0100 Subject: [PATCH 3/4] lxd/network/driver/ovn: Modify setup() to support optional IP addresses Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 66 ++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index d8b9b39714..4e06b123e2 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1371,14 +1371,14 @@ func (n *ovn) setup(update bool) error { } } - if n.getRouterIntPortIPv4Net() != "" { + if validate.IsOneOf(n.getRouterIntPortIPv4Net(), []string{"none", ""}) != nil { routerIntPortIPv4, routerIntPortIPv4Net, err = net.ParseCIDR(n.getRouterIntPortIPv4Net()) if err != nil { return errors.Wrapf(err, "Failed parsing router's internal port IPv4 Net") } } - if n.getRouterIntPortIPv6Net() != "" { + if validate.IsOneOf(n.getRouterIntPortIPv6Net(), []string{"none", ""}) != nil { routerIntPortIPv6, routerIntPortIPv6Net, err = net.ParseCIDR(n.getRouterIntPortIPv6Net()) if err != nil { return errors.Wrapf(err, "Failed parsing router's internal port IPv6 Net") @@ -1509,11 +1509,16 @@ func (n *ovn) setup(update bool) error { } revert.Add(func() { client.LogicalSwitchDelete(n.getIntSwitchName()) }) + var excludeIPV4 []shared.IPRange + if routerIntPortIPv4 != nil { + excludeIPV4 = []shared.IPRange{{Start: routerIntPortIPv4}} + } + // Setup IP allocation config on logical switch. err = client.LogicalSwitchSetIPAllocation(n.getIntSwitchName(), &openvswitch.OVNIPAllocationOpts{ PrefixIPv4: routerIntPortIPv4Net, PrefixIPv6: routerIntPortIPv6Net, - ExcludeIPv4: []shared.IPRange{{Start: routerIntPortIPv4}}, + ExcludeIPv4: excludeIPV4, }) if err != nil { return errors.Wrapf(err, "Failed setting IP allocation settings on internal switch") @@ -1541,40 +1546,41 @@ func (n *ovn) setup(update bool) error { } } - // Create DHCPv4 options for internal switch. - err = client.LogicalSwitchDHCPv4OptionsSet(n.getIntSwitchName(), dhcpv4UUID, routerIntPortIPv4Net, &openvswitch.OVNDHCPv4Opts{ - ServerID: routerIntPortIPv4, - ServerMAC: routerMAC, - Router: routerIntPortIPv4, - RecursiveDNSServer: uplinkNet.dnsIPv4, - DomainName: n.getDomainName(), - LeaseTime: time.Duration(time.Hour * 1), - MTU: bridgeMTU, - }) - if err != nil { - return errors.Wrapf(err, "Failed adding DHCPv4 settings for internal switch") - } - - // Create DHCPv6 options for internal switch. - err = client.LogicalSwitchDHCPv6OptionsSet(n.getIntSwitchName(), dhcpv6UUID, routerIntPortIPv6Net, &openvswitch.OVNDHCPv6Opts{ - ServerID: routerMAC, - RecursiveDNSServer: uplinkNet.dnsIPv6, - DNSSearchList: n.getDNSSearchList(), - }) - if err != nil { - return errors.Wrapf(err, "Failed adding DHCPv6 settings for internal switch") - } - - // Generate internal router port IPs (in CIDR format). + // Internal router port IPs (in CIDR format). intRouterIPs := []*net.IPNet{} + + // Create DHCPv4 options for internal switch. if routerIntPortIPv4Net != nil { + err = client.LogicalSwitchDHCPv4OptionsSet(n.getIntSwitchName(), dhcpv4UUID, routerIntPortIPv4Net, &openvswitch.OVNDHCPv4Opts{ + ServerID: routerIntPortIPv4, + ServerMAC: routerMAC, + Router: routerIntPortIPv4, + RecursiveDNSServer: uplinkNet.dnsIPv4, + DomainName: n.getDomainName(), + LeaseTime: time.Duration(time.Hour * 1), + MTU: bridgeMTU, + }) + if err != nil { + return errors.Wrapf(err, "Failed adding DHCPv4 settings for internal switch") + } + intRouterIPs = append(intRouterIPs, &net.IPNet{ IP: routerIntPortIPv4, Mask: routerIntPortIPv4Net.Mask, }) } + // Create DHCPv6 options for internal switch. if routerIntPortIPv6Net != nil { + err = client.LogicalSwitchDHCPv6OptionsSet(n.getIntSwitchName(), dhcpv6UUID, routerIntPortIPv6Net, &openvswitch.OVNDHCPv6Opts{ + ServerID: routerMAC, + RecursiveDNSServer: uplinkNet.dnsIPv6, + DNSSearchList: n.getDNSSearchList(), + }) + if err != nil { + return errors.Wrapf(err, "Failed adding DHCPv6 settings for internal switch") + } + intRouterIPs = append(intRouterIPs, &net.IPNet{ IP: routerIntPortIPv6, Mask: routerIntPortIPv6Net.Mask, @@ -1582,6 +1588,10 @@ func (n *ovn) setup(update bool) error { } // Create internal router port. + if len(intRouterIPs) <= 0 { + return fmt.Errorf("No IPs defined for network router") + } + err = client.LogicalRouterPortAdd(n.getRouterName(), n.getRouterIntPortName(), routerMAC, intRouterIPs...) if err != nil { return errors.Wrapf(err, "Failed adding internal router port") From 78f3c3e7973932ae216bdb7d90ef0267021ed2a8 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 11:21:20 +0100 Subject: [PATCH 4/4] lxd/network/driver/ovn: Updates instanceDevicePortAdd to support optional IP addresses Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 4e06b123e2..ea1616d269 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1900,7 +1900,7 @@ func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, deviceN } // Get DHCP options IDs. - if n.getRouterIntPortIPv4Net() != "" { + if validate.IsOneOf(n.getRouterIntPortIPv4Net(), []string{"none", ""}) != nil { _, routerIntPortIPv4Net, err := net.ParseCIDR(n.getRouterIntPortIPv4Net()) if err != nil { return "", err @@ -1912,7 +1912,7 @@ func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, deviceN } } - if n.getRouterIntPortIPv6Net() != "" { + if validate.IsOneOf(n.getRouterIntPortIPv6Net(), []string{"none", ""}) != nil { _, routerIntPortIPv6Net, err := net.ParseCIDR(n.getRouterIntPortIPv6Net()) if err != nil { return "", err From noreply at github.com Mon Oct 19 12:29:36 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Mon, 19 Oct 2020 05:29:36 -0700 Subject: [lxc-devel] [lxc/lxc] 35f0c4: sync: switch to new error helpers Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: 35f0c46e0da931d32c297d203831ea5da9bef72c https://github.com/lxc/lxc/commit/35f0c46e0da931d32c297d203831ea5da9bef72c Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/sync.c Log Message: ----------- sync: switch to new error helpers Signed-off-by: Christian Brauner Commit: 5befd767a6cb65e3c08456c73501b7fec63b564c https://github.com/lxc/lxc/commit/5befd767a6cb65e3c08456c73501b7fec63b564c Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/sync.c M src/lxc/sync.h Log Message: ----------- sync: log synchronization states Signed-off-by: Christian Brauner Commit: fbfe5c8208fd8304ee74b2e297585c64a0d6bd81 https://github.com/lxc/lxc/commit/fbfe5c8208fd8304ee74b2e297585c64a0d6bd81 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/conf.c M src/lxc/start.c Log Message: ----------- start: improve devpts fd sending Closes: #3549. Signed-off-by: Christian Brauner Commit: 1593efb5d7e85721b2c9a69dde88e533d2f9cebe https://github.com/lxc/lxc/commit/1593efb5d7e85721b2c9a69dde88e533d2f9cebe Author: Stéphane Graber Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/conf.c M src/lxc/start.c M src/lxc/sync.c M src/lxc/sync.h Log Message: ----------- Merge pull request #3556 from brauner/2020-10-19/fixes startup fixes Compare: https://github.com/lxc/lxc/compare/a282f7792fcc...1593efb5d7e8 From builds at travis-ci.org Mon Oct 19 12:47:53 2020 From: builds at travis-ci.org (Travis CI) Date: Mon, 19 Oct 2020 12:47:53 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7897 (master - 1593efb) In-Reply-To: Message-ID: <5f8d8af84a8ca_13f81eead62cc2254@travis-tasks-865556f6c6-q9w87.mail> Build Update for lxc/lxc ------------------------------------- Build: #7897 Status: Errored Duration: 17 mins and 30 secs Commit: 1593efb (master) Author: Stéphane Graber Message: Merge pull request #3556 from brauner/2020-10-19/fixes startup fixes View the changeset: https://github.com/lxc/lxc/compare/a282f7792fcc...1593efb5d7e8 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737052974?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lxc-bot at linuxcontainers.org Mon Oct 19 15:55:38 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Mon, 19 Oct 2020 08:55:38 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: Only call Validate in FillConfig if state is set Message-ID: <5f8db6fa.1c69fb81.aabd7.4f95SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 361 bytes Desc: not available URL: -------------- next part -------------- From 71caae866af04c142dbb48cc19a4d7a126e3657a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 15:56:33 +0100 Subject: [PATCH] lxd/network/driver/ovn: Only call Validate in FillConfig if state is set Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index ea1616d269..cfc5e7fff2 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1168,7 +1168,7 @@ func (n *ovn) FillConfig(config map[string]string) error { changedConfig = true } - if changedConfig { + if changedConfig && n.state != nil { return n.Validate(config) } From noreply at github.com Mon Oct 19 16:00:24 2020 From: noreply at github.com (Christian Brauner) Date: Mon, 19 Oct 2020 09:00:24 -0700 Subject: [lxc-devel] [lxc/lxc] 01e5af: terminal: safely allocate pts devices from inside ... Message-ID: Branch: refs/heads/stable-4.0 Home: https://github.com/lxc/lxc Commit: 01e5af75dac178c7050029e45e91c22c9c4965da https://github.com/lxc/lxc/commit/01e5af75dac178c7050029e45e91c22c9c4965da Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M doc/api-extensions.md M src/lxc/api_extensions.h M src/lxc/attach.c M src/lxc/commands.c M src/lxc/commands.h M src/lxc/conf.c M src/lxc/conf.h M src/lxc/lxccontainer.c M src/lxc/lxccontainer.h M src/lxc/start.c M src/lxc/terminal.c M src/lxc/terminal.h Log Message: ----------- terminal: safely allocate pts devices from inside the container This was a year long journey which seems to finally have come to an end. Closes: #1620. Signed-off-by: Christian Brauner Commit: 3615d54a5d76a61406389ecb1f622d4f56465e98 https://github.com/lxc/lxc/commit/3615d54a5d76a61406389ecb1f622d4f56465e98 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/macro.h Log Message: ----------- macro: define TIOCGPTPEER if missing Signed-off-by: Christian Brauner Commit: 6e90f17424def3db1c9d4d4bf7153dc9146704c7 https://github.com/lxc/lxc/commit/6e90f17424def3db1c9d4d4bf7153dc9146704c7 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/conf.c Log Message: ----------- conf: use openat() instead of open_tree() Signed-off-by: Christian Brauner Commit: f26fb0b541f335ef4dae971690188da85ccc3d2d https://github.com/lxc/lxc/commit/f26fb0b541f335ef4dae971690188da85ccc3d2d Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- seccomp: don't close the mainloop, simply remove the handler Signed-off-by: Christian Brauner Commit: e300f6e990f0296f7aa6d84e231af66697541e8a https://github.com/lxc/lxc/commit/e300f6e990f0296f7aa6d84e231af66697541e8a Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M doc/api-extensions.md M src/lxc/api_extensions.h M src/lxc/commands.c M src/lxc/commands.h M src/lxc/lxccontainer.c M src/lxc/lxccontainer.h Log Message: ----------- seccomp: add seccomp_notify_fd_active api extension which allows to retrieve an active seccomp notifier fd from a running container. Signed-off-by: Christian Brauner Commit: d50941275f5af9b67193d484785062aae05cff2b https://github.com/lxc/lxc/commit/d50941275f5af9b67193d484785062aae05cff2b Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- seccomp: send notify fd as part of the message Since we haven't made this official api yet: YOLO Signed-off-by: Christian Brauner Commit: f61193f889acee2f5904bfa89e8e280c7a7ed295 https://github.com/lxc/lxc/commit/f61193f889acee2f5904bfa89e8e280c7a7ed295 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M doc/api-extensions.md M src/lxc/api_extensions.h Log Message: ----------- api-extension: add missing seccomp_proxy_send_notify_fd extension Signed-off-by: Christian Brauner Commit: 5f6b921c1a1df91db42bda02cf2ce98f07dbffa1 https://github.com/lxc/lxc/commit/5f6b921c1a1df91db42bda02cf2ce98f07dbffa1 Author: Stéphane Graber Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M templates/lxc-download.in Log Message: ----------- Revert "templates/lxc-download.in: use GPG option --receive-keys instead of --recv-keys" This reverts commit 409040e702f814a167aed5a0e833f4d5c67fd29d. Testing of both options show identical behavior but receive-keys does not exist on older releases, so let's revert this. Closes #3510 Signed-off-by: Stéphane Graber Commit: 0f2503d3114b5d04734723d92ea7a589c2f48bc9 https://github.com/lxc/lxc/commit/0f2503d3114b5d04734723d92ea7a589c2f48bc9 Author: Stéphane Graber Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M templates/lxc-download.in Log Message: ----------- lxc-download: Fix retry loop Closes #3511 Signed-off-by: Stéphane Graber Commit: 36c2ad35c544aa1129d677261afed8a1a9047fb1 https://github.com/lxc/lxc/commit/36c2ad35c544aa1129d677261afed8a1a9047fb1 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M configure.ac M src/lxc/conf.c M src/lxc/syscall_numbers.h M src/lxc/syscall_wrappers.h Log Message: ----------- syscalls: add openat2() Signed-off-by: Christian Brauner Commit: 4e2b86de921ca1036d8ba249006711dbad572904 https://github.com/lxc/lxc/commit/4e2b86de921ca1036d8ba249006711dbad572904 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/utils.c M src/lxc/utils.h Log Message: ----------- utils: add safe_mount_beneath() based on openat2() Signed-off-by: Christian Brauner Commit: 594e5f241c6d075ee728fce1f70010daf1bcfbdf https://github.com/lxc/lxc/commit/594e5f241c6d075ee728fce1f70010daf1bcfbdf Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/conf.c Log Message: ----------- conf: switch mount_autodev() to new safe_mount_beneath() helper Signed-off-by: Christian Brauner Commit: bf5f0d7b1c4b7c10215f626334ff8b952e423837 https://github.com/lxc/lxc/commit/bf5f0d7b1c4b7c10215f626334ff8b952e423837 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/cgroups/cgfsng.c Log Message: ----------- cgfsng: use safe_mount_beneath() Signed-off-by: Christian Brauner Commit: 3eda62ad45f9feb138bde9aa10d83fd7c0b517df https://github.com/lxc/lxc/commit/3eda62ad45f9feb138bde9aa10d83fd7c0b517df Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/conf.c M src/lxc/utils.c M src/lxc/utils.h Log Message: ----------- utils: introduce safe_mount_beneath_at() Signed-off-by: Christian Brauner Commit: fd377ee881233a245043314afb715b1444981d95 https://github.com/lxc/lxc/commit/fd377ee881233a245043314afb715b1444981d95 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/conf.c M src/lxc/conf.h Log Message: ----------- conf: stash file descriptor to root mountpoint in struct lxc_rootfs This way we only need to open it _once_ per container startup. Signed-off-by: Christian Brauner Commit: 3e2ec670878d84551d82214c9ff370a1af483774 https://github.com/lxc/lxc/commit/3e2ec670878d84551d82214c9ff370a1af483774 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/conf.c Log Message: ----------- conf: make use of stashed container mountpoint fd in mount_autodev() Signed-off-by: Christian Brauner Commit: 110fc87362198296d5e022e4a81685383b8e92b9 https://github.com/lxc/lxc/commit/110fc87362198296d5e022e4a81685383b8e92b9 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/file_utils.c M src/lxc/file_utils.h M src/lxc/utils.c Log Message: ----------- file_utils: add exists_dir_at() Signed-off-by: Christian Brauner Commit: 8f4bff49fd94c57091fdefbac513bb257e7b504f https://github.com/lxc/lxc/commit/8f4bff49fd94c57091fdefbac513bb257e7b504f Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/conf.c Log Message: ----------- conf: harden lxc_fill_autodev() via save_mount_beneath_at() Signed-off-by: Christian Brauner Commit: d226c6409084adab1b8d505505f1b0a06878016a https://github.com/lxc/lxc/commit/d226c6409084adab1b8d505505f1b0a06878016a Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/conf.c M src/lxc/conf.h M src/lxc/file_utils.c M src/lxc/file_utils.h Log Message: ----------- conf: move /dev setup to be file descriptor based Signed-off-by: Christian Brauner Commit: 91a5e8b780f139b2d6a3c92f942c7cbed9f3b8f4 https://github.com/lxc/lxc/commit/91a5e8b780f139b2d6a3c92f942c7cbed9f3b8f4 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/attach.c M src/lxc/conf.c M src/lxc/file_utils.c M src/lxc/file_utils.h M src/lxc/start.c M src/lxc/terminal.c M src/lxc/terminal.h Log Message: ----------- terminal: harden terminal allocation Signed-off-by: Christian Brauner Commit: e6a4e7c126810b806aa537528a65ef2adc8fc3bc https://github.com/lxc/lxc/commit/e6a4e7c126810b806aa537528a65ef2adc8fc3bc Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/attach.c M src/lxc/attach.h M src/lxc/conf.c M src/lxc/lsm/apparmor.c M src/lxc/lsm/lsm.c M src/lxc/lsm/lsm.h M src/lxc/lsm/nop.c M src/lxc/lsm/selinux.c M src/lxc/start.c M src/lxc/start.h M src/lxc/utils.c M src/lxc/utils.h M src/tests/attach.c Log Message: ----------- lsm: rework lsm handling Signed-off-by: Christian Brauner Commit: 491436791489b2bfd1dc37211ec558036e602e56 https://github.com/lxc/lxc/commit/491436791489b2bfd1dc37211ec558036e602e56 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/lsm/apparmor.c Log Message: ----------- lsm: use atomic in ase we're used multi-threaded Signed-off-by: Christian Brauner Commit: 74f0c66dc4bb80a6b706c0e3595ab6e167246412 https://github.com/lxc/lxc/commit/74f0c66dc4bb80a6b706c0e3595ab6e167246412 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/attach.c M src/lxc/attach.h M src/lxc/conf.c M src/lxc/lsm/apparmor.c M src/lxc/lsm/lsm.c M src/lxc/lsm/lsm.h M src/lxc/lsm/nop.c M src/lxc/lsm/selinux.c M src/lxc/start.c M src/lxc/start.h M src/tests/attach.c Log Message: ----------- lsm: remove the need for atomic operations Signed-off-by: Christian Brauner Commit: 31ef03b52f621e905165d653443423ae5f6685dc https://github.com/lxc/lxc/commit/31ef03b52f621e905165d653443423ae5f6685dc Author: Arjun Ramachandrula Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M doc/pam_cgfs.sgml.in Log Message: ----------- Updated documentation to reflect lack of support for pure cgroupv2 Signed-off-by: Arjun Ramachandrula Commit: 509eae3a682559015e414d42e9b685b6618aa43f https://github.com/lxc/lxc/commit/509eae3a682559015e414d42e9b685b6618aa43f Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/cgroups/cgfsng.c Log Message: ----------- cgfsng: fix cgroup attach cgroup creation cgroups/cgfsng.c: In function ‘cgroup_attach_leaf.constprop’: cgroups/cgfsng.c:2221:10: error: writing 1 byte into a region of size 0 [-Werror=stringop-overflow=] 2221 | *slash = '\0'; | ~~~~~~~^~~~~~ cgroups/cgfsng.c:2213:8: note: at offset -13 to object ‘attach_cgroup’ with size 23 declared here 2213 | char attach_cgroup[STRLITERALLEN(".lxc-1000/cgroup.procs") + 1]; | ^~~~~~~~~~~~~ cgroups/cgfsng.c:2229:10: error: writing 1 byte into a region of size 0 [-Werror=stringop-overflow=] 2229 | *slash = '/'; | ~~~~~~~^~~~~ cgroups/cgfsng.c:2213:8: note: at offset -13 to object ‘attach_cgroup’ with size 23 declared here 2213 | char attach_cgroup[STRLITERALLEN(".lxc-1000/cgroup.procs") + 1]; | ^~~~~~~~~~~~~ cgroups/cgfsng.c:2229:10: error: writing 1 byte into a region of size 0 [-Werror=stringop-overflow=] 2229 | *slash = '/'; | ~~~~~~~^~~~~ cgroups/cgfsng.c:2213:8: note: at offset -13 to object ‘attach_cgroup’ with size 23 declared here 2213 | char attach_cgroup[STRLITERALLEN(".lxc-1000/cgroup.procs") + 1]; | ^~~~~~~~~~~~~ Link: https://launchpadlibrarian.net/494354168/buildlog_ubuntu-groovy-armhf.lxc_1%3A4.0.4-0ubuntu1_BUILDING.txt.gz Signed-off-by: Christian Brauner Commit: 783dba47991f5cfe6a571406f67d311e50863867 https://github.com/lxc/lxc/commit/783dba47991f5cfe6a571406f67d311e50863867 Author: graysky Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M config/init/systemd/lxc.service.in Log Message: ----------- remove deprecated options in lxc.service fixes #3527 Signed-off-by: graysky Commit: f6a72ee4b6911742f778ea453573454b8c872ed7 https://github.com/lxc/lxc/commit/f6a72ee4b6911742f778ea453573454b8c872ed7 Author: Pranay Kr. Srivastava Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/utils.c Log Message: ----------- Check only rootfs as filesystem type When detecting if rootfs is on ramfs instead of checking "- rootfs rootfs" which is the " - " information only check the file system type. This is due to a change introduced in kernel where ramfs file system doesn't set the device to "rootfs" but instead mark it as "none". By making sure we only check for "rootfs" as the file system name we also offer backward compatibility with earlier kernels as well. The kernel commit that introduced this change was commit f32356261d44d580649a7abce1156d15d49cf20f Author: David Howells Date: Mon Mar 25 16:38:31 2019 +0000 vfs: Convert ramfs, shmem, tmpfs, devtmpfs, rootfs to use the new mount API Signed-off-by: Pranay Kr. Srivastava Commit: f3d2aa897f1d94e18cded99def33802581a26446 https://github.com/lxc/lxc/commit/f3d2aa897f1d94e18cded99def33802581a26446 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/cgroups/cgfsng.c Log Message: ----------- cgroups: fix armhf builds Link: https://launchpadlibrarian.net/494473462/buildlog_ubuntu-groovy-armhf.lxc_1%3A4.0.4-0ubuntu2_BUILDING.txt.gz Signed-off-by: Christian Brauner Commit: 8c23afc3cf3fa9f61b760b2c79a273e2ffbe7296 https://github.com/lxc/lxc/commit/8c23afc3cf3fa9f61b760b2c79a273e2ffbe7296 Author: wujing Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/conf.c Log Message: ----------- remove useless parameters Signed-off-by: wujing Commit: a836df0484d20f0a1911016d483045068aab9767 https://github.com/lxc/lxc/commit/a836df0484d20f0a1911016d483045068aab9767 Author: Scott Parlane Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/attach.c Log Message: ----------- avoid a NULL pointer dereference in lxc-attach Seems to appear when stderr is a terminal and not stdin or stdout. Signed-off-by: Scott Parlane Commit: 1748c4bd53b6e979bab15aabcc4865f075cae579 https://github.com/lxc/lxc/commit/1748c4bd53b6e979bab15aabcc4865f075cae579 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/terminal.c M src/lxc/terminal.h Log Message: ----------- terminal: introduce lxc_terminal_signal_sigmask_safe_blocked() Signed-off-by: Christian Brauner Commit: f59f2f9ea77d4f0bc45148263b0ac3ff94039bd6 https://github.com/lxc/lxc/commit/f59f2f9ea77d4f0bc45148263b0ac3ff94039bd6 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/attach.c Log Message: ----------- attach: use lxc_terminal_signal_sigmask_safe_blocked() Signed-off-by: Christian Brauner Commit: 422820d5f3db6126b819684d76f3c1f333aa7c58 https://github.com/lxc/lxc/commit/422820d5f3db6126b819684d76f3c1f333aa7c58 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/commands.c Log Message: ----------- commands: don't fail if unfreeze fails We can e.g. fail the unfreeze because the freezer cgroup is not available and then we erronously report that stopping the container failed. Closes: #3471. Signed-off-by: Christian Brauner Commit: 5e43318908f9bfdb02a09efabc735f2317e40f2f https://github.com/lxc/lxc/commit/5e43318908f9bfdb02a09efabc735f2317e40f2f Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/cmd/lxc_usernsexec.c Log Message: ----------- lxc-usernsexec: setgroups() similar to other places shouldn't fail on EPERM FAIL: lxc-tests: lxc-test-usernsexec (1s) --- as test-userns executing /tmp/autopkgtest.waGEXj/build.Hm3/src/src/tests/lxc-test-usernsexec uid=1001 gid=1001 name=test-userns subuid=165536 subgid=165536 ver=1:4.0.4-0ubuntu3 lxc-utils=1:4.0.4-0ubuntu3 kver=5.8.0-19-generic USERNSEXEC=lxc-usernsexec nouidgid: PASS myuidgid: FAIL - runtest failed 1 $ lxc-usernsexec -mu:0:1001:1 -mg:0:1001:1 -- /tmp/autopkgtest.waGEXj/build.Hm3/src/src/tests/lxc-test-usernsexec inside f0 lxc 20200914222824.562 ERROR utils - utils.c:lxc_setgroups:1363 - Operation not permitted - Failed to setgroups() kid 73112 is gone 1 subuidgid: PASS bothsets: PASS mismatch: PASS ERRORS: myuidgid --- Reported-by: Seth Forshee Signed-off-by: Christian Brauner Commit: 9b43bce0720e1be059991313cbaa4c0b06508ef0 https://github.com/lxc/lxc/commit/9b43bce0720e1be059991313cbaa4c0b06508ef0 Author: Mingli Yu Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: A config/init/systemd/lxc-monitord.service.in M config/init/systemd/lxc at .service.in Log Message: ----------- Remove obsolete setting regarding the Standard Output The Standard output type "syslog" is obsolete, causing a warning since systemd version 246 [1]. Please consider using "journal" or "journal+console" [1] https://github.com/systemd/systemd/blob/master/NEWS#L202 Signed-off-by: Mingli Yu Commit: 7241d0f40ff731309aba1a6c8b23ea7afcfb20e6 https://github.com/lxc/lxc/commit/7241d0f40ff731309aba1a6c8b23ea7afcfb20e6 Author: Ruben Jenster Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- seccomp: Check if syscall is supported on compat architecture. Signed-off-by: Ruben Jenster Commit: 95cc56cde57dab5818511aaea20eec007d3978ba https://github.com/lxc/lxc/commit/95cc56cde57dab5818511aaea20eec007d3978ba Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- seccomp: log invalid seccomp notify ids Signed-off-by: Christian Brauner Commit: 6778323df2ea11650daa8ec3f9f26ffc3fcb8fe8 https://github.com/lxc/lxc/commit/6778323df2ea11650daa8ec3f9f26ffc3fcb8fe8 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- seccomp: improve default notification sending Signed-off-by: Christian Brauner Commit: 8d3bcf63fc16ddccddfd05b8a5973d24b0156d8d https://github.com/lxc/lxc/commit/8d3bcf63fc16ddccddfd05b8a5973d24b0156d8d Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- seccomp: fix compilation on powerpc Link: https://launchpadlibrarian.net/502200189/buildlog_snap_ubuntu_bionic_ppc64el_lxd-latest-edge_BUILDING.txt.gz Signed-off-by: Christian Brauner Commit: dab1ce7fbcd4b7574f0af9d8134ba0e616b990c0 https://github.com/lxc/lxc/commit/dab1ce7fbcd4b7574f0af9d8134ba0e616b990c0 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/sync.c Log Message: ----------- sync: switch to new error helpers Signed-off-by: Christian Brauner Commit: 49f049653664e98a5884a73832c7e53e383dd9c0 https://github.com/lxc/lxc/commit/49f049653664e98a5884a73832c7e53e383dd9c0 Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/sync.c M src/lxc/sync.h Log Message: ----------- sync: log synchronization states Signed-off-by: Christian Brauner Commit: 90a3b1da817784346594cdfe4669ae7a74dda93a https://github.com/lxc/lxc/commit/90a3b1da817784346594cdfe4669ae7a74dda93a Author: Christian Brauner Date: 2020-10-19 (Mon, 19 Oct 2020) Changed paths: M src/lxc/conf.c M src/lxc/start.c Log Message: ----------- start: improve devpts fd sending Closes: #3549. Signed-off-by: Christian Brauner Compare: https://github.com/lxc/lxc/compare/531e01280365...90a3b1da8177 From lxc-bot at linuxcontainers.org Mon Oct 19 16:13:08 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Mon, 19 Oct 2020 09:13:08 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: Removes OVN ipv4.routes.external and ipv6.routes.external Message-ID: <5f8dbb14.1c69fb81.a4d65.2452SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 337 bytes Desc: not available URL: -------------- next part -------------- From 71caae866af04c142dbb48cc19a4d7a126e3657a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 15:56:33 +0100 Subject: [PATCH 01/15] lxd/network/driver/ovn: Only call Validate in FillConfig if state is set Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index ea1616d269..cfc5e7fff2 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1168,7 +1168,7 @@ func (n *ovn) FillConfig(config map[string]string) error { changedConfig = true } - if changedConfig { + if changedConfig && n.state != nil { return n.Validate(config) } From f6add9084341a88c0847398eb4db5610e7e5f1bd Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 15:56:15 +0100 Subject: [PATCH 02/15] lxd/db/projects: Adds GetProject function Signed-off-by: Thomas Parrott --- lxd/db/projects.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lxd/db/projects.go b/lxd/db/projects.go index cf03765ea5..ff613611d1 100644 --- a/lxd/db/projects.go +++ b/lxd/db/projects.go @@ -189,3 +189,22 @@ func (c *ClusterTx) InitProjectWithoutImages(project string) error { _, err = c.tx.Exec(stmt, defaultProfileID) return err } + +// GetProject returns the project with the given key. +func (c *Cluster) GetProject(projectName string) (*api.Project, error) { + var err error + var p *api.Project + err = c.Transaction(func(tx *ClusterTx) error { + p, err = tx.GetProject(projectName) + if err != nil { + return err + } + + return nil + }) + if err != nil { + return nil, err + } + + return p, nil +} From fa2da560919d515ecd24a2aebc40b4a213c34fea Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 13:56:53 +0100 Subject: [PATCH 03/15] lxd/network/driver/ovn: Converts instance port functions to exported So they can be accessed by OVN NIC directly. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index cfc5e7fff2..25780813e1 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1887,8 +1887,8 @@ func (n *ovn) getInstanceDevicePortName(instanceID int, deviceName string) openv return openvswitch.OVNSwitchPort(fmt.Sprintf("%s-%d-%s", n.getIntSwitchInstancePortPrefix(), instanceID, deviceName)) } -// instanceDevicePortAdd adds an instance device port to the internal logical switch and returns the port name. -func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, deviceName string, mac net.HardwareAddr, ips []net.IP, internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error) { +// InstanceDevicePortAdd adds an instance device port to the internal logical switch and returns the port name. +func (n *ovn) InstanceDevicePortAdd(instanceID int, instanceName string, deviceName string, mac net.HardwareAddr, ips []net.IP, internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error) { var dhcpV4ID, dhcpv6ID string revert := revert.New() @@ -2065,8 +2065,8 @@ func (n *ovn) instanceDevicePortAdd(instanceID int, instanceName string, deviceN return instancePortName, nil } -// instanceDevicePortIPs returns the dynamically allocated IPs for a device port. -func (n *ovn) instanceDevicePortDynamicIPs(instanceID int, deviceName string) ([]net.IP, error) { +// InstanceDevicePortDynamicIPs returns the dynamically allocated IPs for a device port. +func (n *ovn) InstanceDevicePortDynamicIPs(instanceID int, deviceName string) ([]net.IP, error) { instancePortName := n.getInstanceDevicePortName(instanceID, deviceName) client, err := n.getClient() @@ -2077,8 +2077,8 @@ func (n *ovn) instanceDevicePortDynamicIPs(instanceID int, deviceName string) ([ return client.LogicalSwitchPortDynamicIPs(instancePortName) } -// instanceDevicePortDelete deletes an instance device port from the internal logical switch. -func (n *ovn) instanceDevicePortDelete(instanceID int, deviceName string, internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) error { +// InstanceDevicePortDelete deletes an instance device port from the internal logical switch. +func (n *ovn) InstanceDevicePortDelete(instanceID int, deviceName string, internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) error { instancePortName := n.getInstanceDevicePortName(instanceID, deviceName) client, err := n.getClient() From d5351219834cbb9bb12faba3cf653f0751765898 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 14:02:16 +0100 Subject: [PATCH 04/15] lxd/network/driver/ovn: Removes ipv4.routes.external and ipv6.routes.external Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 25780813e1..91463e5477 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -133,13 +133,11 @@ func (n *ovn) Validate(config map[string]string) error { return validate.Optional(validate.IsNetworkAddressCIDRV6)(value) }, - "ipv6.dhcp.stateful": validate.Optional(validate.IsBool), - "ipv4.routes.external": validate.Optional(validate.IsNetworkV4List), - "ipv6.routes.external": validate.Optional(validate.IsNetworkV6List), - "ipv4.nat": validate.Optional(validate.IsBool), - "ipv6.nat": validate.Optional(validate.IsBool), - "dns.domain": validate.IsAny, - "dns.search": validate.IsAny, + "ipv6.dhcp.stateful": validate.Optional(validate.IsBool), + "ipv4.nat": validate.Optional(validate.IsBool), + "ipv6.nat": validate.Optional(validate.IsBool), + "dns.domain": validate.IsAny, + "dns.search": validate.IsAny, // Volatile keys populated automatically as needed. ovnVolatileUplinkIPv4: validate.Optional(validate.IsNetworkAddressV4), @@ -232,28 +230,6 @@ func (n *ovn) Validate(config map[string]string) error { } } - // Check IP external routes are within the uplink network's routes and project's subnet restrictions. - if config["ipv4.routes.external"] != "" || config["ipv6.routes.external"] != "" { - // Parse and validate our external routes. - for _, k := range []string{"ipv4.routes.external", "ipv6.routes.external"} { - if config[k] == "" { - continue - } - - routeSubnetList, err := SubnetParseAppend([]*net.IPNet{}, strings.Split(config[k], ",")...) - if err != nil { - return err - } - - for _, routeSubnet := range routeSubnetList { - err = n.validateExternalSubnet(uplinkRoutes, projectRestrictedSubnets, routeSubnet) - if err != nil { - return err - } - } - } - } - return nil } From e1668b6f4f92365854ad48eb4ee243186c87fa15 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 17:00:06 +0100 Subject: [PATCH 05/15] lxc/network/driver/ovn: Adds projectRestrictedSubnets and uplinkRoutes functions Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 55 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 91463e5477..1e537c7413 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -78,6 +78,61 @@ func (n *ovn) Info() Info { } } +// uplinkRoutes parses ipv4.routes and ipv6.routes settings for a named uplink network into a slice of *net.IPNet. +func (n *ovn) uplinkRoutes(uplinkNetworkName string) ([]*net.IPNet, error) { + _, uplink, err := n.state.Cluster.GetNetworkInAnyState(project.Default, uplinkNetworkName) + if err != nil { + return nil, err + } + + var uplinkRoutes []*net.IPNet + for _, k := range []string{"ipv4.routes", "ipv6.routes"} { + if uplink.Config[k] == "" { + continue + } + + uplinkRoutes, err = SubnetParseAppend(uplinkRoutes, strings.Split(uplink.Config[k], ",")...) + if err != nil { + return nil, err + } + } + + return uplinkRoutes, nil +} + +// projectRestrictedSubnets parses the restrict.networks.subnets project setting and returns slice of *net.IPNet. +// Returns nil slice if no project restrictions, or empty slice if no allowed subnets. +func (n *ovn) projectRestrictedSubnets(p *api.Project, uplinkNetworkName string) ([]*net.IPNet, error) { + // Parse project's restricted subnets. + var projectRestrictedSubnets []*net.IPNet // Nil value indicates not restricted. + if shared.IsTrue(p.Config["restricted"]) && p.Config["restricted.networks.subnets"] != "" { + projectRestrictedSubnets = []*net.IPNet{} // Empty slice indicates no allowed subnets. + + for _, subnetRaw := range strings.Split(p.Config["restricted.networks.subnets"], ",") { + subnetParts := strings.SplitN(strings.TrimSpace(subnetRaw), ":", 2) + if len(subnetParts) != 2 { + return nil, fmt.Errorf(`Project subnet %q invalid, must be in the format of ":"`, subnetRaw) + } + + subnetUplinkName := subnetParts[0] + subnetStr := subnetParts[1] + + if subnetUplinkName != uplinkNetworkName { + continue // Only include subnets for our uplink. + } + + _, restrictedSubnet, err := net.ParseCIDR(subnetStr) + if err != nil { + return nil, err + } + + projectRestrictedSubnets = append(projectRestrictedSubnets, restrictedSubnet) + } + } + + return projectRestrictedSubnets, nil +} + // validateExternalSubnet checks the supplied ipNet is allowed within the uplink routes and project // restricted subnets. If projectRestrictedSubnets is nil, then it is not checked as this indicates project has // no restrictions. Whereas if uplinkRoutes is nil/empty then this will always return an error. From bbfce066807e2491abe1bd281cccc0ac6c8f7b26 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 17:00:30 +0100 Subject: [PATCH 06/15] lxd/network/driver/ovn: Simplifies Validate by using separate data loader functions Also removes a duplicate project load query from DB. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 60 ++++++--------------------------------- 1 file changed, 9 insertions(+), 51 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 1e537c7413..8d7b55111f 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -205,69 +205,27 @@ func (n *ovn) Validate(config map[string]string) error { } // Load the project to get uplink network restrictions. - var projectRow *api.Project - err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error { - projectRow, err = tx.GetProject(n.project) - if err != nil { - return err - } - - return nil - }) + p, err := n.state.Cluster.GetProject(n.project) if err != nil { - return errors.Wrapf(err, "Failed to load IP route restrictions for project %q", n.project) + return errors.Wrapf(err, "Failed to load network restrictions from project %q", n.project) } // Check uplink network is valid and allowed in project. - uplinkNetworkName, err := n.validateUplinkNetwork(config["network"]) + uplinkNetworkName, err := n.validateUplinkNetwork(p, config["network"]) if err != nil { return err } - // Load the uplink network to get uplink routes. - _, uplink, err := n.state.Cluster.GetNetworkInAnyState(project.Default, uplinkNetworkName) + // Get uplink routes. + uplinkRoutes, err := n.uplinkRoutes(uplinkNetworkName) if err != nil { return err } - // Parse uplink route subnets. - var uplinkRoutes []*net.IPNet - for _, k := range []string{"ipv4.routes", "ipv6.routes"} { - if uplink.Config[k] == "" { - continue - } - - uplinkRoutes, err = SubnetParseAppend(uplinkRoutes, strings.Split(uplink.Config[k], ",")...) - if err != nil { - return err - } - } - - // Parse project's restricted subnets. - var projectRestrictedSubnets []*net.IPNet // Nil value indicates not restricted. - if shared.IsTrue(projectRow.Config["restricted"]) && projectRow.Config["restricted.networks.subnets"] != "" { - projectRestrictedSubnets = []*net.IPNet{} // Empty slice indicates no allowed subnets. - - for _, subnetRaw := range strings.Split(projectRow.Config["restricted.networks.subnets"], ",") { - subnetParts := strings.SplitN(strings.TrimSpace(subnetRaw), ":", 2) - if len(subnetParts) != 2 { - return fmt.Errorf(`Project subnet %q invalid, must be in the format of ":"`, subnetRaw) - } - - uplinkName := subnetParts[0] - subnetStr := subnetParts[1] - - if uplinkName != uplink.Name { - continue // Only include subnets for our uplink. - } - - _, restrictedSubnet, err := net.ParseCIDR(subnetStr) - if err != nil { - return err - } - - projectRestrictedSubnets = append(projectRestrictedSubnets, restrictedSubnet) - } + // Get project restricted routes. + projectRestrictedSubnets, err := n.projectRestrictedSubnets(p, uplinkNetworkName) + if err != nil { + return err } // If NAT disabled, check subnets are within the uplink network's routes and project's subnet restrictions. From d84254815bcb4e822fad4ef67f6a2c84a4c5a76e Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 17:01:14 +0100 Subject: [PATCH 07/15] lxd/network/driver/ovn: Passes project into allowedUplinkNetworks Rather than load again from DB. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 8d7b55111f..f401f08fd0 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1180,7 +1180,7 @@ func (n *ovn) Create(clientType cluster.ClientType) error { } // allowedUplinkNetworks returns a list of allowed networks to use as uplinks based on project restrictions. -func (n *ovn) allowedUplinkNetworks() ([]string, error) { +func (n *ovn) allowedUplinkNetworks(p *api.Project) ([]string, error) { // Uplink networks are always from the default project. networks, err := n.state.Cluster.GetNetworks(project.Default) if err != nil { @@ -1200,34 +1200,20 @@ func (n *ovn) allowedUplinkNetworks() ([]string, error) { } } - // Load the project to get uplink network restrictions. - var project *api.Project - err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error { - project, err = tx.GetProject(n.project) - if err != nil { - return err - } - - return nil - }) - if err != nil { - return nil, errors.Wrapf(err, "Failed to load restrictions for project %q", n.project) - } - // If project is not restricted, return full network list. - if !shared.IsTrue(project.Config["restricted"]) { + if !shared.IsTrue(p.Config["restricted"]) { return networks, nil } allowedNetworks := []string{} // There are no allowed networks if restricted.networks.uplinks is not set. - if project.Config["restricted.networks.uplinks"] == "" { + if p.Config["restricted.networks.uplinks"] == "" { return allowedNetworks, nil } // Parse the allowed uplinks and return any that are present in the actual defined networks. - allowedRestrictedUplinks := strings.Split(project.Config["restricted.networks.uplinks"], ",") + allowedRestrictedUplinks := strings.Split(p.Config["restricted.networks.uplinks"], ",") for _, allowedRestrictedUplink := range allowedRestrictedUplinks { allowedRestrictedUplink = strings.TrimSpace(allowedRestrictedUplink) From bfd7ac77861bdc804e742948aa0c7c12b38946c2 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 17:01:43 +0100 Subject: [PATCH 08/15] lxd/network/driver/ovn: Passes project into validateUplinkNetwork To avoid additional query. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index f401f08fd0..9ea0806cf2 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1229,8 +1229,8 @@ func (n *ovn) allowedUplinkNetworks(p *api.Project) ([]string, error) { // validateUplinkNetwork checks if uplink network is allowed, and if empty string is supplied then tries to select // an uplink network from the allowedUplinkNetworks() list if there is only one allowed network. // Returns chosen uplink network name to use. -func (n *ovn) validateUplinkNetwork(uplinkNetworkName string) (string, error) { - allowedUplinkNetworks, err := n.allowedUplinkNetworks() +func (n *ovn) validateUplinkNetwork(p *api.Project, uplinkNetworkName string) (string, error) { + allowedUplinkNetworks, err := n.allowedUplinkNetworks(p) if err != nil { return "", err } From 44e6104b2a3acccb81bbc4a6c2c8f5eca43da058 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 17:02:09 +0100 Subject: [PATCH 09/15] lxd/network/driver/ovn: Load project in setup() to pass to n.validateUplinkNetwork() Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 9ea0806cf2..8f568c54c5 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1276,8 +1276,14 @@ func (n *ovn) setup(update bool) error { // Record updated config so we can store back into DB and n.config variable. updatedConfig := make(map[string]string) + // Load the project to get uplink network restrictions. + p, err := n.state.Cluster.GetProject(n.project) + if err != nil { + return errors.Wrapf(err, "Failed to load network restrictions from project %q", n.project) + } + // Check project restrictions and get uplink network to use. - uplinkNetwork, err := n.validateUplinkNetwork(n.config["network"]) + uplinkNetwork, err := n.validateUplinkNetwork(p, n.config["network"]) if err != nil { return err } From 3efb1adc6d469353639768c632549ca846b65d38 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 17:02:30 +0100 Subject: [PATCH 10/15] lxd/network/driver/ovn: Adds InstanceDevicePortValidateExternalRoutes function To validate a OVN NIC's external routes against an OVN network's uplink routes and project's restricted subnet. Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 8f568c54c5..1898ad2f4a 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1868,6 +1868,36 @@ func (n *ovn) getInstanceDevicePortName(instanceID int, deviceName string) openv return openvswitch.OVNSwitchPort(fmt.Sprintf("%s-%d-%s", n.getIntSwitchInstancePortPrefix(), instanceID, deviceName)) } +// InstanceDevicePortValidateExternalRoutes validates the external routes for an OVN instance port. +func (n *ovn) InstanceDevicePortValidateExternalRoutes(externalRoutes []*net.IPNet) error { + // Load the project to get uplink network restrictions. + p, err := n.state.Cluster.GetProject(n.project) + if err != nil { + return errors.Wrapf(err, "Failed to load network restrictions from project %q", n.project) + } + + // Get uplink routes. + uplinkRoutes, err := n.uplinkRoutes(n.config["network"]) + if err != nil { + return err + } + + // Get project restricted routes. + projectRestrictedSubnets, err := n.projectRestrictedSubnets(p, n.config["network"]) + if err != nil { + return err + } + + for _, externalRoute := range externalRoutes { + err = n.validateExternalSubnet(uplinkRoutes, projectRestrictedSubnets, externalRoute) + if err != nil { + return err + } + } + + return nil +} + // InstanceDevicePortAdd adds an instance device port to the internal logical switch and returns the port name. func (n *ovn) InstanceDevicePortAdd(instanceID int, instanceName string, deviceName string, mac net.HardwareAddr, ips []net.IP, internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error) { var dhcpV4ID, dhcpv6ID string From 131a644a8507295a9c25882e88a48ba952e125bf Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 13:55:55 +0100 Subject: [PATCH 11/15] lxd/network/network/utils/ovn: Remvoes unused functions Signed-off-by: Thomas Parrott --- lxd/network/network_utils_ovn.go | 42 -------------------------------- 1 file changed, 42 deletions(-) delete mode 100644 lxd/network/network_utils_ovn.go diff --git a/lxd/network/network_utils_ovn.go b/lxd/network/network_utils_ovn.go deleted file mode 100644 index 41b8e89d27..0000000000 --- a/lxd/network/network_utils_ovn.go +++ /dev/null @@ -1,42 +0,0 @@ -package network - -import ( - "fmt" - "net" - - "github.com/lxc/lxd/lxd/network/openvswitch" -) - -// OVNInstanceDevicePortAdd adds a logical port to the OVN network's internal switch and returns the logical -// port name for use linking an OVS port on the integration bridge to the logical switch port. -func OVNInstanceDevicePortAdd(network Network, instanceID int, instanceName string, deviceName string, mac net.HardwareAddr, ips []net.IP, internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error) { - // Check network is of type OVN. - n, ok := network.(*ovn) - if !ok { - return "", fmt.Errorf("Network is not OVN type") - } - - return n.instanceDevicePortAdd(instanceID, instanceName, deviceName, mac, ips, internalRoutes, externalRoutes) -} - -// OVNInstanceDevicePortDynamicIPs gets a logical port's dynamic IPs stored in the OVN network's internal switch. -func OVNInstanceDevicePortDynamicIPs(network Network, instanceID int, deviceName string) ([]net.IP, error) { - // Check network is of type OVN. - n, ok := network.(*ovn) - if !ok { - return nil, fmt.Errorf("Network is not OVN type") - } - - return n.instanceDevicePortDynamicIPs(instanceID, deviceName) -} - -// OVNInstanceDevicePortDelete deletes a logical port from the OVN network's internal switch. -func OVNInstanceDevicePortDelete(network Network, instanceID int, deviceName string, internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) error { - // Check network is of type OVN. - n, ok := network.(*ovn) - if !ok { - return fmt.Errorf("Network is not OVN type") - } - - return n.instanceDevicePortDelete(instanceID, deviceName, internalRoutes, externalRoutes) -} From 08415e9d128c15abf020089325ba5c090ceb0e7d Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 13:58:09 +0100 Subject: [PATCH 12/15] lxd/device/nic/ovn: Adds ovnNet interface and use OVN instance port functions directly from network Signed-off-by: Thomas Parrott --- lxd/device/nic_ovn.go | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go index 091888ab60..99c49ea622 100644 --- a/lxd/device/nic_ovn.go +++ b/lxd/device/nic_ovn.go @@ -24,10 +24,20 @@ import ( log "github.com/lxc/lxd/shared/log15" ) +// ovnNet defines an interface for accessing instance specific functions on OVN network. +type ovnNet interface { + network.Network + + InstanceDevicePortValidateExternalRoutes(externalRoutes []*net.IPNet) error + InstanceDevicePortAdd(instanceID int, instanceName string, deviceName string, mac net.HardwareAddr, ips []net.IP, internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error) + InstanceDevicePortDelete(instanceID int, deviceName string, internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) error + InstanceDevicePortDynamicIPs(instanceID int, deviceName string) ([]net.IP, error) +} + type nicOVN struct { deviceCommon - network network.Network // Populated in validateConfig(). + network ovnNet // Populated in validateConfig(). } // getIntegrationBridgeName returns the OVS integration bridge to use. @@ -91,7 +101,12 @@ func (d *nicOVN) validateConfig(instConf instance.ConfigReader) error { } } - d.network = n // Stored loaded instance for use by other functions. + ovnNet, ok := n.(ovnNet) + if !ok { + return fmt.Errorf("Network is not OVN type") + } + + d.network = ovnNet // Stored loaded instance for use by other functions. netConfig := d.network.Config() if d.config["ipv4.address"] != "" { @@ -317,14 +332,12 @@ func (d *nicOVN) Start() (*deviceConfig.RunConfig, error) { } // Add new OVN logical switch port for instance. - logicalPortName, err := network.OVNInstanceDevicePortAdd(d.network, d.inst.ID(), d.inst.Name(), d.name, mac, ips, internalRoutes, externalRoutes) + logicalPortName, err := d.network.InstanceDevicePortAdd(d.inst.ID(), d.inst.Name(), d.name, mac, ips, internalRoutes, externalRoutes) if err != nil { return nil, errors.Wrapf(err, "Failed adding OVN port") } - revert.Add(func() { - network.OVNInstanceDevicePortDelete(d.network, d.inst.ID(), d.name, internalRoutes, externalRoutes) - }) + revert.Add(func() { d.network.InstanceDevicePortDelete(d.inst.ID(), d.name, internalRoutes, externalRoutes) }) // Attach host side veth interface to bridge. integrationBridge, err := d.getIntegrationBridgeName() @@ -455,7 +468,7 @@ func (d *nicOVN) Stop() (*deviceConfig.RunConfig, error) { } } - err = network.OVNInstanceDevicePortDelete(d.network, d.inst.ID(), d.name, internalRoutes, externalRoutes) + err = d.network.InstanceDevicePortDelete(d.inst.ID(), d.name, internalRoutes, externalRoutes) if err != nil { // Don't fail here as we still want the postStop hook to run to clean up the local veth pair. d.logger.Error("Failed to remove OVN device port", log.Ctx{"err": err}) @@ -531,7 +544,7 @@ func (d *nicOVN) State() (*api.InstanceStateNetwork, error) { // OVN only supports dynamic IP allocation if neither IPv4 or IPv6 are statically set. if d.config["ipv4.address"] == "" && d.config["ipv6.address"] == "" { - dynamicIPs, err := network.OVNInstanceDevicePortDynamicIPs(d.network, d.inst.ID(), d.name) + dynamicIPs, err := d.network.InstanceDevicePortDynamicIPs(d.inst.ID(), d.name) if err == nil { for _, dynamicIP := range dynamicIPs { family := "inet" From b9b1964381a0851d00c552fac3519dd02daf28ad Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 17:03:40 +0100 Subject: [PATCH 13/15] lxd/device/nic/ovn: Removes validation of external routes against network's external routes Signed-off-by: Thomas Parrott --- lxd/device/nic_ovn.go | 50 ------------------------------------------- 1 file changed, 50 deletions(-) diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go index 99c49ea622..16b1dff6cd 100644 --- a/lxd/device/nic_ovn.go +++ b/lxd/device/nic_ovn.go @@ -145,56 +145,6 @@ func (d *nicOVN) validateConfig(instConf instance.ConfigReader) error { } } - // Check IP external routes are within the network's external routes. - if d.config["ipv4.routes.external"] != "" || d.config["ipv6.routes.external"] != "" { - // Parse network external route subnets. - var networkRoutes []*net.IPNet - for _, k := range []string{"ipv4.routes.external", "ipv6.routes.external"} { - if netConfig[k] == "" { - continue - } - - networkRoutes, err = network.SubnetParseAppend(networkRoutes, strings.Split(netConfig[k], ",")...) - if err != nil { - return err - } - } - - // Parse and validate our external routes. - for _, k := range []string{"ipv4.routes.external", "ipv6.routes.external"} { - if d.config[k] == "" { - continue - } - - externalRoutes, err := network.SubnetParseAppend([]*net.IPNet{}, strings.Split(d.config[k], ",")...) - if err != nil { - return err - } - - for _, externalRoute := range externalRoutes { - rOnes, rBits := externalRoute.Mask.Size() - if rBits > 32 && rOnes < 122 { - return fmt.Errorf("External route %q is too large. Maximum size for IPv6 external route is /122", externalRoute.String()) - } else if rOnes < 26 { - return fmt.Errorf("External route %q is too large. Maximum size for IPv4 external route is /26", externalRoute.String()) - } - - // Check that the external route is within the network's routes. - foundMatch := false - for _, networkRoute := range networkRoutes { - if network.SubnetContains(networkRoute, externalRoute) { - foundMatch = true - break - } - } - - if !foundMatch { - return fmt.Errorf("Network %q doesn't contain %q in its external routes", n.Name(), externalRoute.String()) - } - } - } - } - // Apply network level config options to device config before validation. d.config["mtu"] = fmt.Sprintf("%s", netConfig["bridge.mtu"]) From 78d4ec8e820ad4f41f42887565b2f112442f3fff Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 17:04:11 +0100 Subject: [PATCH 14/15] lxd/device/nic/ovn: Validate NICs external routes using d.network.InstanceDevicePortValidateExternalRoutes Signed-off-by: Thomas Parrott --- lxd/device/nic_ovn.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go index 16b1dff6cd..f743083165 100644 --- a/lxd/device/nic_ovn.go +++ b/lxd/device/nic_ovn.go @@ -156,6 +156,35 @@ func (d *nicOVN) validateConfig(instConf instance.ConfigReader) error { return err } + // Check IP external routes are within the network's external routes. + var externalRoutes []*net.IPNet + for _, k := range []string{"ipv4.routes.external", "ipv6.routes.external"} { + if d.config[k] == "" { + continue + } + + externalRoutes, err = network.SubnetParseAppend(externalRoutes, strings.Split(d.config[k], ",")...) + if err != nil { + return err + } + } + + if len(externalRoutes) > 0 { + for _, externalRoute := range externalRoutes { + rOnes, rBits := externalRoute.Mask.Size() + if rBits > 32 && rOnes < 122 { + return fmt.Errorf("External route %q is too large. Maximum size for IPv6 external route is /122", externalRoute.String()) + } else if rOnes < 26 { + return fmt.Errorf("External route %q is too large. Maximum size for IPv4 external route is /26", externalRoute.String()) + } + } + + err = d.network.InstanceDevicePortValidateExternalRoutes(externalRoutes) + if err != nil { + return err + } + } + return nil } From 4a2a968c0e4e330d00d2c65d434bf9101f883c43 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 17:06:19 +0100 Subject: [PATCH 15/15] doc/networks: Removes ipv4.routes.external and ipv6.routes.external from ovn network Signed-off-by: Thomas Parrott --- doc/networks.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/networks.md b/doc/networks.md index d03da41f9d..dc6e109a2e 100644 --- a/doc/networks.md +++ b/doc/networks.md @@ -298,11 +298,9 @@ dns.domain | string | - | lxd dns.search | string | - | - | Full comma separated domain search list, defaulting to `dns.domain` value ipv4.address | string | standard mode | random unused subnet | IPv4 address for the bridge (CIDR notation). Use "none" to turn off IPv4 or "auto" to generate a new one ipv4.nat | boolean | ipv4 address | false | Whether to NAT (will default to true if unset and a random ipv4.address is generated) -ipv4.routes.external | string | ipv4 address | - | Comma separated list of additional external IPv4 CIDR subnets that are allowed for OVN NICs ipv4.routes.external setting ipv6.address | string | standard mode | random unused subnet | IPv6 address for the bridge (CIDR notation). Use "none" to turn off IPv6 or "auto" to generate a new one ipv6.dhcp.stateful | boolean | ipv6 dhcp | false | Whether to allocate addresses using DHCP ipv6.nat | boolean | ipv6 address | false | Whether to NAT (will default to true if unset and a random ipv6.address is generated) -ipv6.routes.external | string | ipv6 address | - | Comma separated list of additional external IPv6 CIDR subnets that are allowed for OVN NICs ipv6.routes.external setting network | string | - | - | Uplink network to use for external network access ## network: physical From builds at travis-ci.org Mon Oct 19 16:17:31 2020 From: builds at travis-ci.org (Travis CI) Date: Mon, 19 Oct 2020 16:17:31 +0000 Subject: [lxc-devel] Passed: lxc/lxc#7898 (stable-4.0 - 90a3b1d) In-Reply-To: Message-ID: <5f8dbc15b641d_13fd5c09aaaa84034cf@travis-tasks-865556f6c6-tzzxf.mail> Build Update for lxc/lxc ------------------------------------- Build: #7898 Status: Passed Duration: 14 mins and 4 secs Commit: 90a3b1d (stable-4.0) Author: Christian Brauner Message: start: improve devpts fd sending Closes: #3549. Signed-off-by: Christian Brauner View the changeset: https://github.com/lxc/lxc/compare/531e01280365...90a3b1da8177 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737126381?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lxc-bot at linuxcontainers.org Mon Oct 19 16:51:15 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Mon, 19 Oct 2020 09:51:15 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: Fix project restricted subnets check in OVN network validateExternalSubnet Message-ID: <5f8dc403.1c69fb81.5bf70.582cSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 361 bytes Desc: not available URL: -------------- next part -------------- From bccedf2bdde9459fcf7bc5dd1862f6f651a92679 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Mon, 19 Oct 2020 17:44:15 +0100 Subject: [PATCH] lxd/network/driver/ovn: Fix project restricted subnets check in validateExternalSubnet Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index ea1616d269..cf1b91ed82 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -86,7 +86,7 @@ func (n *ovn) validateExternalSubnet(uplinkRoutes []*net.IPNet, projectRestricte if projectRestrictedSubnets != nil { foundMatch := false for _, projectRestrictedSubnet := range projectRestrictedSubnets { - if !SubnetContains(projectRestrictedSubnet, ipNet) { + if SubnetContains(projectRestrictedSubnet, ipNet) { foundMatch = true break } From lxc-bot at linuxcontainers.org Tue Oct 20 07:45:15 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Tue, 20 Oct 2020 00:45:15 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Support virtio-fs Message-ID: <5f8e958b.1c69fb81.f7e59.59c3SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 8bcad318eea963979740453fcf9e28884f0fffa4 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Tue, 20 Oct 2020 08:39:23 +0200 Subject: [PATCH 1/5] lxd/instance/drivers: Change memory backend This changes the memory backend to memory-backend-memfd. Also, this turns on `share`. Both are required for virtio-fs. Signed-off-by: Thomas Hipp --- lxd/instance/drivers/driver_qemu_templates.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lxd/instance/drivers/driver_qemu_templates.go b/lxd/instance/drivers/driver_qemu_templates.go index 29d4aa6b40..17051c1510 100644 --- a/lxd/instance/drivers/driver_qemu_templates.go +++ b/lxd/instance/drivers/driver_qemu_templates.go @@ -279,9 +279,10 @@ mem-path = "{{$hugepages}}" prealloc = "on" discard-data = "on" {{- else}} -qom-type = "memory-backend-ram" +qom-type = "memory-backend-memfd" {{- end }} size = "{{$memory}}M" +share = "on" [numa] type = "node" From a4aeded8bcd15f278acd58e8d5ea2427c718c79f Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Tue, 20 Oct 2020 09:15:48 +0200 Subject: [PATCH 2/5] lxd/instance/drivers: Add qemu virtio-fs template Signed-off-by: Thomas Hipp --- lxd/instance/drivers/driver_qemu_templates.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lxd/instance/drivers/driver_qemu_templates.go b/lxd/instance/drivers/driver_qemu_templates.go index 17051c1510..306e6a0412 100644 --- a/lxd/instance/drivers/driver_qemu_templates.go +++ b/lxd/instance/drivers/driver_qemu_templates.go @@ -541,3 +541,15 @@ driver = "usb-host" bus = "qemu_usb.0" hostdevice = "{{.hostDevice}}" `)) + +var qemuVirtioFSDev = template.Must(template.New("qemuVirtioFSDev").Parse(` +# Virtio FS +[chardev "qemu_virtiofs-{{.devName}}"] +backend = "socket" +path = "{{.path}}" + +[device "dev-lxd_{{.devName}}"] +driver = "vhost-user-fs-pci" +chardev = "qemu_virtiofs-{{.devName}}" +tag = "{{.tag}}" +`)) From c384d07f23d503153d577d3bb84ac93a01894888 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Tue, 20 Oct 2020 09:24:11 +0200 Subject: [PATCH 3/5] lxd/device/disk: Support virtio-fs Signed-off-by: Thomas Hipp --- lxd/device/disk.go | 64 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/lxd/device/disk.go b/lxd/device/disk.go index 4f728007e7..4b5797cecf 100644 --- a/lxd/device/disk.go +++ b/lxd/device/disk.go @@ -152,7 +152,7 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error { // contains the name of the storage volume, not the path where it is mounted. So only check // for the existence of "source" when "pool" is empty and ceph type source not being used. if d.config["pool"] == "" && d.config["source"] != "" && d.config["source"] != diskSourceCloudInit && d.isRequired(d.config) && !shared.PathExists(shared.HostPath(d.config["source"])) && - !strings.HasPrefix(d.config["source"], "ceph:") && !strings.HasPrefix(d.config["source"], "cephfs:") { + !strings.HasPrefix(d.config["source"], "ceph:") && !strings.HasPrefix(d.config["source"], "cephfs:") && !strings.HasPrefix(d.config["source"], "virtiofs:") { return fmt.Errorf("Missing source %q for disk %q", d.config["source"], d.name) } @@ -449,6 +449,53 @@ func (d *disk) startVM() (*deviceConfig.RunConfig, error) { DevName: d.name, }, } + } else if strings.HasPrefix(d.config["source"], "virtiofs:") { + fields := strings.SplitN(d.config["source"], ":", 2) + srcPath := fields[1] + // Create the socket in this directory instead of the devices directory. QEMU will otherwise + // fail with "Permission Denied" which is probably caused by qemu being called with --chroot. + sockPath := filepath.Join(d.inst.Path(), fmt.Sprintf("%s.sock", d.name)) + logPath := filepath.Join(d.inst.LogPath(), fmt.Sprintf("disk.%s.log", d.name)) + + // Remove old socket if needed. + os.Remove(sockPath) + + // Start the virtiofsd process in non-daemon mode. + proc, err := subprocess.NewProcess("/usr/lib/qemu/virtiofsd", []string{fmt.Sprintf("--socket-path=%s", sockPath), "-o", fmt.Sprintf("source=%s", srcPath)}, logPath, logPath) + if err != nil { + return nil, err + } + + err = proc.Start() + if err != nil { + return nil, errors.Wrapf(err, "Failed to start virtiofsd for device %q", d.name) + } + + revert.Add(func() { proc.Stop() }) + + pidPath := filepath.Join(d.inst.DevicesPath(), fmt.Sprintf("%s.pid", d.name)) + err = proc.Save(pidPath) + if err != nil { + return nil, errors.Wrapf(err, "Failed to save virtiofsd state for device %q", d.name) + } + + // Wait for socket file to exist (as otherwise qemu can race the creation of this file). + for i := 0; i < 10; i++ { + if shared.PathExists(sockPath) { + break + } + + time.Sleep(50 * time.Millisecond) + } + + runConf.Mounts = []deviceConfig.MountEntryItem{ + { + DevName: d.name, + DevPath: sockPath, + FSType: "virtiofs", + TargetPath: d.config["path"], + }, + } } else { srcPath := shared.HostPath(d.config["source"]) var err error @@ -1139,13 +1186,24 @@ func (d *disk) stopVM() (*deviceConfig.RunConfig, error) { } err = proc.Stop() - if err != nil { + // virtiofsd will terminate automatically once the VM has stopped. We therefore should only + // return an error if it's a running process. + if err != nil && err != subprocess.ErrNotRunning { return &deviceConfig.RunConfig{}, err } // Remove PID file and socket file. os.Remove(pidPath) - os.Remove(filepath.Join(d.inst.DevicesPath(), fmt.Sprintf("%s.sock", d.name))) + + var sockPath string + + if strings.HasPrefix(d.config["source"], "virtiofs:") { + sockPath = filepath.Join(d.inst.Path(), fmt.Sprintf("%s.sock", d.name)) + } else { + sockPath = filepath.Join(d.inst.DevicesPath(), fmt.Sprintf("%s.sock", d.name)) + } + + os.Remove(sockPath) } runConf := deviceConfig.RunConfig{ From 7279b2569967d36cd5d7705de6189bbedb6232a8 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Tue, 20 Oct 2020 09:24:25 +0200 Subject: [PATCH 4/5] lxd/instance/drivers: Support virtio-fs Signed-off-by: Thomas Hipp --- lxd/instance/drivers/driver_qemu.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 1abaddbbb2..446ddc3f97 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -1823,7 +1823,7 @@ func (vm *qemu) generateQemuConfigFile(busName string, devConfs []*deviceConfig. for _, drive := range runConf.Mounts { if drive.TargetPath == "/" { err = vm.addRootDriveConfig(sb, bootIndexes, drive) - } else if drive.FSType == "9p" { + } else if drive.FSType == "9p" || drive.FSType == "virtiofs" { err = vm.addDriveDirConfig(sb, bus, fdFiles, &agentMounts, drive) } else { err = vm.addDriveConfig(sb, bootIndexes, drive) @@ -2056,6 +2056,14 @@ func (vm *qemu) addDriveDirConfig(sb *strings.Builder, bus *qemuBus, fdFiles *[] devBus, devAddr, multi := bus.allocate(busFunctionGroup9p) + if driveConf.FSType == "virtiofs" { + return qemuVirtioFSDev.Execute(sb, map[string]interface{}{ + "devName": driveConf.DevName, + "path": driveConf.DevPath, + "tag": mountTag, + }) + } + // For read only shares, do not use proxy. if shared.StringInSlice("ro", driveConf.Opts) { return qemuDriveDir.Execute(sb, map[string]interface{}{ From 6240c1951f02d8cf063eda8dbc56e5eab7c5a3d2 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Tue, 20 Oct 2020 09:24:57 +0200 Subject: [PATCH 5/5] doc: Add virtio-fs Signed-off-by: Thomas Hipp --- doc/instances.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/instances.md b/doc/instances.md index 10e4630a33..ca209e7a4a 100644 --- a/doc/instances.md +++ b/doc/instances.md @@ -662,6 +662,11 @@ Example command. lxc config device add config disk source=cloud-init:config ``` +- Virtio-fs: A shared file system that lets virtual machines access a directory tree on the host. It is designed to offer local file system semantics and performance. +``` +lxc config device add config disk source=virtiofs:/ path=/virtiofs +``` + Currently only the root disk (path=/) and config drive (source=cloud-init:config) are supported with virtual machines. From lxc-bot at linuxcontainers.org Tue Oct 20 08:09:06 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Tue, 20 Oct 2020 01:09:06 -0700 (PDT) Subject: [lxc-devel] [lxd/stable-4.0] Doc: Re-organises NIC device type docs introducing section about network property Message-ID: <5f8e9b22.1c69fb81.dbb38.129bSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 648 bytes Desc: not available URL: -------------- next part -------------- From 5626dd4362c5ac65039c76cc4485c66396fb035e Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 09:04:42 +0100 Subject: [PATCH] doc/instances: Re-organises NIC device type docs introducing section about network property Signed-off-by: Thomas Parrott --- doc/instances.md | 190 ++++++++++++++++++++++++----------------------- 1 file changed, 99 insertions(+), 91 deletions(-) diff --git a/doc/instances.md b/doc/instances.md index 7efdf5dd71..9290de18d2 100644 --- a/doc/instances.md +++ b/doc/instances.md @@ -250,25 +250,74 @@ To do so, just add a none type device with the same name of the one you wish to It can be added in a profile being applied after the profile it originated from or directly on the instance. ### Type: nic -LXD supports different kind of network devices: +LXD supports several different kinds of network devices (referred to as Network Interface Controller or NIC). - - [physical](#nictype-physical): Straight physical device passthrough from the host. The targeted device will vanish from the host and appear in the instance. - - [bridged](#nictype-bridged): Uses an existing bridge on the host and creates a virtual device pair to connect the host bridge to the instance. - - [macvlan](#nictype-macvlan): Sets up a new network device based on an existing one but using a different MAC address. - - [ipvlan](#nictype-ipvlan): Sets up a new network device based on an existing one using the same MAC address but a different IP. - - [p2p](#nictype-p2p): Creates a virtual device pair, putting one side in the instance and leaving the other side on the host. - - [sriov](#nictype-sriov): Passes a virtual function of an SR-IOV enabled physical network device into the instance. - - [routed](#nictype-routed): Creates a virtual device pair to connect the host to the instance and sets up static routes and proxy ARP/NDP entries to allow the instance to join the network of a designated parent interface. +When adding a network device to an instance, there are two ways to specify the type of device you want to add; +either by specifying the `nictype` property or using the `network` property. -Different network interface types have different additional properties. +#### Specifying a NIC using the `network` property -Each possible `nictype` value is documented below along with the relevant properties for nics of that type. +When specifying the `network` property, the NIC is linked to an existing managed network and the `nictype` is +automatically detected based on the network's type. -#### nictype: physical +Some of the NICs properties are inherited from the network rather than being customisable for each NIC. + +These are detailed in the "Managed" column in the NIC specific sections below. + +#### NICs Available: + +See the NIC's settings below for details about which properties are available. + +The following NICs can be specified using the `nictype` or `network` properties: + + - [bridged](#nic-bridged): Uses an existing bridge on the host and creates a virtual device pair to connect the host bridge to the instance. + +The following NICs can be specified using only the `nictype` property: + + - [macvlan](#nic-macvlan): Sets up a new network device based on an existing one but using a different MAC address. + - [sriov](#nic-sriov): Passes a virtual function of an SR-IOV enabled physical network device into the instance. + - [physical](#nic-physical): Straight physical device passthrough from the host. The targeted device will vanish from the host and appear in the instance. + - [ipvlan](#nic-ipvlan): Sets up a new network device based on an existing one using the same MAC address but a different IP. + - [p2p](#nic-p2p): Creates a virtual device pair, putting one side in the instance and leaving the other side on the host. + - [routed](#nic-routed): Creates a virtual device pair to connect the host to the instance and sets up static routes and proxy ARP/NDP entries to allow the instance to join the network of a designated parent interface. + +#### nic: bridged Supported instance types: container, VM -Straight physical device passthrough from the host. The targeted device will vanish from the host and appear in the instance. +Selected using: `nictype`, `network` + +Uses an existing bridge on the host and creates a virtual device pair to connect the host bridge to the instance. + +Device configuration properties: + +Key | Type | Default | Required | Managed | Description +:-- | :-- | :-- | :-- | :-- | :-- +parent | string | - | yes | yes | The name of the host device +network | string | - | yes | no | The LXD network to link device to (instead of parent) +name | string | kernel assigned | no | no | The name of the interface inside the instance +mtu | integer | parent MTU | no | yes | The MTU of the new interface +hwaddr | string | randomly assigned | no | no | The MAC address of the new interface +host\_name | string | randomly assigned | no | no | The name of the interface inside the host +limits.ingress | string | - | no | no | I/O limit in bit/s for incoming traffic (various suffixes supported, see below) +limits.egress | string | - | no | no | I/O limit in bit/s for outgoing traffic (various suffixes supported, see below) +limits.max | string | - | no | no | Same as modifying both limits.ingress and limits.egress +ipv4.address | string | - | no | no | An IPv4 address to assign to the instance through DHCP +ipv6.address | string | - | no | no | An IPv6 address to assign to the instance through DHCP +ipv4.routes | string | - | no | no | Comma delimited list of IPv4 static routes to add on host to nic +ipv6.routes | string | - | no | no | Comma delimited list of IPv6 static routes to add on host to nic +security.mac\_filtering | boolean | false | no | no | Prevent the instance from spoofing another's MAC address +security.ipv4\_filtering | boolean | false | no | no | Prevent the instance from spoofing another's IPv4 address (enables mac\_filtering) +security.ipv6\_filtering | boolean | false | no | no | Prevent the instance from spoofing another's IPv6 address (enables mac\_filtering) +maas.subnet.ipv4 | string | - | no | yes | MAAS IPv4 subnet to register the instance in +maas.subnet.ipv6 | string | - | no | yes | MAAS IPv6 subnet to register the instance in +boot.priority | integer | - | no | no | Boot priority for VMs (higher boots first) + +#### nic: macvlan + +Supported instance types: container, VM + +Sets up a new network device based on an existing one but using a different MAC address. Device configuration properties: @@ -283,41 +332,31 @@ maas.subnet.ipv4 | string | - | no | MAAS IPv4 maas.subnet.ipv6 | string | - | no | MAAS IPv6 subnet to register the instance in boot.priority | integer | - | no | Boot priority for VMs (higher boots first) -#### nictype: bridged +#### nic: sriov Supported instance types: container, VM -Uses an existing bridge on the host and creates a virtual device pair to connect the host bridge to the instance. +Passes a virtual function of an SR-IOV enabled physical network device into the instance. Device configuration properties: -Key | Type | Default | Required | Description -:-- | :-- | :-- | :-- | :-- -parent | string | - | yes | The name of the host device -network | string | - | yes | The LXD network to link device to (instead of parent) -name | string | kernel assigned | no | The name of the interface inside the instance -mtu | integer | parent MTU | no | The MTU of the new interface -hwaddr | string | randomly assigned | no | The MAC address of the new interface -host\_name | string | randomly assigned | no | The name of the interface inside the host -limits.ingress | string | - | no | I/O limit in bit/s for incoming traffic (various suffixes supported, see below) -limits.egress | string | - | no | I/O limit in bit/s for outgoing traffic (various suffixes supported, see below) -limits.max | string | - | no | Same as modifying both limits.ingress and limits.egress -ipv4.address | string | - | no | An IPv4 address to assign to the instance through DHCP -ipv6.address | string | - | no | An IPv6 address to assign to the instance through DHCP -ipv4.routes | string | - | no | Comma delimited list of IPv4 static routes to add on host to nic -ipv6.routes | string | - | no | Comma delimited list of IPv6 static routes to add on host to nic -security.mac\_filtering | boolean | false | no | Prevent the instance from spoofing another's MAC address -security.ipv4\_filtering | boolean | false | no | Prevent the instance from spoofing another's IPv4 address (enables mac\_filtering) -security.ipv6\_filtering | boolean | false | no | Prevent the instance from spoofing another's IPv6 address (enables mac\_filtering) -maas.subnet.ipv4 | string | - | no | MAAS IPv4 subnet to register the instance in -maas.subnet.ipv6 | string | - | no | MAAS IPv6 subnet to register the instance in -boot.priority | integer | - | no | Boot priority for VMs (higher boots first) - -#### nictype: macvlan +Key | Type | Default | Required | Description +:-- | :-- | :-- | :-- | :-- +parent | string | - | yes | The name of the host device +name | string | kernel assigned | no | The name of the interface inside the instance +mtu | integer | kernel assigned | no | The MTU of the new interface +hwaddr | string | randomly assigned | no | The MAC address of the new interface +security.mac\_filtering | boolean | false | no | Prevent the instance from spoofing another's MAC address +vlan | integer | - | no | The VLAN ID to attach to +maas.subnet.ipv4 | string | - | no | MAAS IPv4 subnet to register the instance in +maas.subnet.ipv6 | string | - | no | MAAS IPv6 subnet to register the instance in +boot.priority | integer | - | no | Boot priority for VMs (higher boots first) + +#### nic: physical Supported instance types: container, VM -Sets up a new network device based on an existing one but using a different MAC address. +Straight physical device passthrough from the host. The targeted device will vanish from the host and appear in the instance. Device configuration properties: @@ -332,7 +371,7 @@ maas.subnet.ipv4 | string | - | no | MAAS IPv4 maas.subnet.ipv6 | string | - | no | MAAS IPv6 subnet to register the instance in boot.priority | integer | - | no | Boot priority for VMs (higher boots first) -#### nictype: ipvlan +#### nic: ipvlan Supported instance types: container @@ -373,7 +412,7 @@ ipv6.address | string | - | no | Comma deli ipv6.gateway | string | auto | no | Whether to add an automatic default IPv6 gateway, can be "auto" or "none" vlan | integer | - | no | The VLAN ID to attach to -#### nictype: p2p +#### nic: p2p Supported instance types: container, VM @@ -394,27 +433,7 @@ ipv4.routes | string | - | no | Comma deli ipv6.routes | string | - | no | Comma delimited list of IPv6 static routes to add on host to nic boot.priority | integer | - | no | Boot priority for VMs (higher boots first) -#### nictype: sriov - -Supported instance types: container, VM - -Passes a virtual function of an SR-IOV enabled physical network device into the instance. - -Device configuration properties: - -Key | Type | Default | Required | Description -:-- | :-- | :-- | :-- | :-- -parent | string | - | yes | The name of the host device -name | string | kernel assigned | no | The name of the interface inside the instance -mtu | integer | kernel assigned | no | The MTU of the new interface -hwaddr | string | randomly assigned | no | The MAC address of the new interface -security.mac\_filtering | boolean | false | no | Prevent the instance from spoofing another's MAC address -vlan | integer | - | no | The VLAN ID to attach to -maas.subnet.ipv4 | string | - | no | MAAS IPv4 subnet to register the instance in -maas.subnet.ipv6 | string | - | no | MAAS IPv6 subnet to register the instance in -boot.priority | integer | - | no | Boot priority for VMs (higher boots first) - -#### nictype: routed +#### nic: routed Supported instance types: container @@ -478,41 +497,31 @@ ipv6.host\_address | string | fe80::1 | no | The IPv6 a vlan | integer | - | no | The VLAN ID to attach to #### bridged, macvlan or ipvlan for connection to physical network -The `bridged`, `macvlan` and `ipvlan` interface types can both be used to connect -to an existing physical network. -`macvlan` effectively lets you fork your physical NIC, getting a second -interface that's then used by the instance. This saves you from -creating a bridge device and veth pairs and usually offers better -performance than a bridge. +The `bridged`, `macvlan` and `ipvlan` interface types can be used to connect to an existing physical network. + +`macvlan` effectively lets you fork your physical NIC, getting a second interface that's then used by the instance. +This saves you from creating a bridge device and veth pairs and usually offers better performance than a bridge. -The downside to this is that macvlan devices while able to communicate -between themselves and to the outside, aren't able to talk to their -parent device. This means that you can't use macvlan if you ever need -your instances to talk to the host itself. +The downside to this is that macvlan devices while able to communicate between themselves and to the outside, aren't able to talk to their parent device. +This means that you can't use macvlan if you ever need your instances to talk to the host itself. -In such case, a bridge is preferable. A bridge will also let you use mac -filtering and I/O limits which cannot be applied to a macvlan device. +In such case, a bridge is preferable. A bridge will also let you use mac filtering and I/O limits which cannot be applied to a macvlan device. -`ipvlan` is similar to `macvlan`, with the difference being that the forked device has IPs -statically assigned to it and inherits the parent's MAC address on the network. +`ipvlan` is similar to `macvlan`, with the difference being that the forked device has IPs statically assigned to it and inherits the parent's MAC address on the network. #### SR-IOV -The `sriov` interface type supports SR-IOV enabled network devices. These -devices associate a set of virtual functions (VFs) with the single physical -function (PF) of the network device. PFs are standard PCIe functions. VFs on -the other hand are very lightweight PCIe functions that are optimized for data -movement. They come with a limited set of configuration capabilities to prevent -changing properties of the PF. Given that VFs appear as regular PCIe devices to -the system they can be passed to instances just like a regular physical -device. The `sriov` interface type expects to be passed the name of an SR-IOV -enabled network device on the system via the `parent` property. LXD will then -check for any available VFs on the system. By default LXD will allocate the -first free VF it finds. If it detects that either none are enabled or all -currently enabled VFs are in use it will bump the number of supported VFs to -the maximum value and use the first free VF. If all possible VFs are in use or -the kernel or card doesn't support incrementing the number of VFs LXD will -return an error. To create a `sriov` network device use: +The `sriov` interface type supports SR-IOV enabled network devices. +These devices associate a set of virtual functions (VFs) with the single physical function (PF) of the network device. +PFs are standard PCIe functions. VFs on the other hand are very lightweight PCIe functions that are optimized for data movement. +They come with a limited set of configuration capabilities to prevent changing properties of the PF. +Given that VFs appear as regular PCIe devices to the system they can be passed to instances just like a regular physical device. +The `sriov` interface type expects to be passed the name of an SR-IOV enabled network device on the system via the `parent` property. +LXD will then check for any available VFs on the system. By default LXD will allocate the first free VF it finds. +If it detects that either none are enabled or all currently enabled VFs are in use it will bump the number of supported VFs to the maximum value and use the first free VF. +If all possible VFs are in use or the kernel or card doesn't support incrementing the number of VFs LXD will return an error. + +To create a `sriov` network device use: ``` lxc config device add nic nictype=sriov parent= @@ -521,7 +530,6 @@ lxc config device add nic nictype=sriov parent= A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 406 bytes Desc: not available URL: -------------- next part -------------- From ef2dfa8748815befa7f6705798c23d7445cb4691 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 09:30:13 +0100 Subject: [PATCH 1/3] lxd/images: Fixes ineffectual assign warning Signed-off-by: Thomas Parrott --- lxd/images.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/images.go b/lxd/images.go index 39c7df8d75..d97e759408 100644 --- a/lxd/images.go +++ b/lxd/images.go @@ -241,7 +241,7 @@ func imgPostInstanceInfo(d *Daemon, r *http.Request, req api.ImagesPost, op *ope Tracker: &ioprogress.ProgressTracker{ Handler: func(value, speed int64) { percent := int64(0) - processed := int64(0) + var processed int64 if totalSize > 0 { percent = value From a4e89992943eb88dd8d1d1c010b332f626738170 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 09:31:05 +0100 Subject: [PATCH 2/3] lxd/resources/usb: Fixes ineffectual assign warning Signed-off-by: Thomas Parrott --- lxd/resources/usb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/resources/usb.go b/lxd/resources/usb.go index 6479d252fc..fc88c459dc 100644 --- a/lxd/resources/usb.go +++ b/lxd/resources/usb.go @@ -186,7 +186,7 @@ func GetUSB() (*api.ResourcesUSB, error) { return nil, errors.Wrapf(err, "Failed to parse class ID %q", content) } - ok := false + var ok bool class, ok = usbid.Classes[usbid.ClassCode(iface.ClassID)] if ok { From 9aa5cfd3840415368d71064868391a570976c8fb Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 09:32:13 +0100 Subject: [PATCH 3/3] lxd/storage/drivers/driver/lvm/volumes: Fixes ineffectual assign warning Signed-off-by: Thomas Parrott --- lxd/storage/drivers/driver_lvm_volumes.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lxd/storage/drivers/driver_lvm_volumes.go b/lxd/storage/drivers/driver_lvm_volumes.go index fbe1ef16da..e761fa2582 100644 --- a/lxd/storage/drivers/driver_lvm_volumes.go +++ b/lxd/storage/drivers/driver_lvm_volumes.go @@ -433,12 +433,9 @@ func (d *lvm) GetVolumeDiskPath(vol Volume) (string, error) { // MountVolume mounts a volume. Returns true if this volume was our mount. func (d *lvm) MountVolume(vol Volume, op *operations.Operation) (bool, error) { - var err error - activated := false - // Activate LVM volume if needed. volDevPath := d.lvmDevPath(d.config["lvm.vg_name"], vol.volType, vol.contentType, vol.name) - activated, err = d.activateVolume(volDevPath) + activated, err := d.activateVolume(volDevPath) if err != nil { return false, err } From lxc-bot at linuxcontainers.org Tue Oct 20 08:33:56 2020 From: lxc-bot at linuxcontainers.org (gbiggs on Github) Date: Tue, 20 Oct 2020 01:33:56 -0700 (PDT) Subject: [lxc-devel] [distrobuilder/master] Remove out-of-place statement in actions documentation Message-ID: <5f8ea0f4.1c69fb81.60d03.ef5fSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 310 bytes Desc: not available URL: -------------- next part -------------- From bb419063dad557d5d9ebe4c4c9ea53e5261e0c35 Mon Sep 17 00:00:00 2001 From: Geoffrey Biggs Date: Tue, 20 Oct 2020 17:33:25 +0900 Subject: [PATCH] Remove out-of-place statement Signed-off-by: Geoffrey Biggs --- doc/actions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/actions.md b/doc/actions.md index 03220db..88ea5c8 100644 --- a/doc/actions.md +++ b/doc/actions.md @@ -25,7 +25,7 @@ The above list also shows the order in which the actions are processed. After the root filesystem has been unpacked, all `post-unpack` actions are run. -After the package manager has updated all packages, (given that `packages.update` is `true`), all `post-update` all `post-packages` actions are run. +After the package manager has updated all packages, (given that `packages.update` is `true`), all `post-update` actions are run. After the package manager has installed the requested packages, all `post-packages` actions are run. For more on `packages`, see [packages](packages.md). From lxc-bot at linuxcontainers.org Tue Oct 20 11:34:43 2020 From: lxc-bot at linuxcontainers.org (brauner on Github) Date: Tue, 20 Oct 2020 04:34:43 -0700 (PDT) Subject: [lxc-devel] [lxc/master] conf: always send response to parent waiting for devptfs_fd Message-ID: <5f8ecb53.1c69fb81.b8069.fe60SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 591 bytes Desc: not available URL: -------------- next part -------------- From 68f3899e4aad1695fd31e5da47dcf3897d2ae2d9 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 20 Oct 2020 13:02:00 +0200 Subject: [PATCH] conf: always send response to parent waiting for devptfs_fd When no devpts devices are requested we used to return early but did not send a response to the parent. This is a problem because the parent will be waiting for a devpts fd to be sent. Make sure to always send a response. Signed-off-by: Christian Brauner --- src/lxc/conf.c | 34 ++++++++++++++++++++++++++-------- src/lxc/conf.h | 2 ++ src/lxc/start.c | 5 +---- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index b8058ffdce..e006ea4391 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1477,7 +1477,23 @@ static const struct id_map *find_mapped_nsid_entry(const struct lxc_conf *conf, return retmap; } -static int lxc_setup_devpts(struct lxc_handler *handler) +int lxc_setup_devpts_parent(struct lxc_handler *handler) +{ + int ret; + + if (handler->conf->pty_max <= 0) + return 0; + + ret = lxc_abstract_unix_recv_fds(handler->data_sock[1], &handler->conf->devpts_fd, 1, + &handler->conf->devpts_fd, sizeof(handler->conf->devpts_fd)); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to receive devpts fd from child"); + + TRACE("Received devpts file descriptor %d from child", handler->conf->devpts_fd); + return 0; +} + +static int lxc_setup_devpts_child(struct lxc_handler *handler) { __do_close int devpts_fd = -EBADF; int ret; @@ -1533,13 +1549,7 @@ static int lxc_setup_devpts(struct lxc_handler *handler) if (devpts_fd < 0) { devpts_fd = -EBADF; TRACE("Failed to create detached devpts mount"); - ret = lxc_abstract_unix_send_fds(sock, NULL, 0, &devpts_fd, sizeof(int)); - } else { - ret = lxc_abstract_unix_send_fds(sock, &devpts_fd, 1, NULL, 0); } - if (ret < 0) - return log_error_errno(-1, errno, "Failed to send devpts fd to parent"); - TRACE("Sent devpts file descriptor %d to parent", devpts_fd); /* Remove any pre-existing /dev/ptmx file. */ ret = remove("/dev/ptmx"); @@ -1575,6 +1585,14 @@ static int lxc_setup_devpts(struct lxc_handler *handler) return log_error_errno(-1, errno, "Failed to create symlink from \"/dev/ptmx\" to \"/dev/pts/ptmx\""); DEBUG("Created symlink from \"/dev/ptmx\" to \"/dev/pts/ptmx\""); + if (devpts_fd < 0) + ret = lxc_abstract_unix_send_fds(sock, NULL, 0, &devpts_fd, sizeof(int)); + else + ret = lxc_abstract_unix_send_fds(sock, &devpts_fd, 1, NULL, 0); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to send devpts fd to parent"); + + TRACE("Sent devpts file descriptor %d to parent", devpts_fd); return 0; } @@ -3392,7 +3410,7 @@ int lxc_setup(struct lxc_handler *handler) if (lxc_conf->autodev > 0) (void)lxc_setup_boot_id(); - ret = lxc_setup_devpts(handler); + ret = lxc_setup_devpts_child(handler); if (ret < 0) return log_error(-1, "Failed to setup new devpts instance"); diff --git a/src/lxc/conf.h b/src/lxc/conf.h index cd54b3fe0f..ba06d42dc0 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -490,4 +490,6 @@ static inline int chown_mapped_root(const char *path, const struct lxc_conf *con return userns_exec_mapped_root(path, -EBADF, conf); } +__hidden int lxc_setup_devpts_parent(struct lxc_handler *handler); + #endif /* __LXC_CONF_H */ diff --git a/src/lxc/start.c b/src/lxc/start.c index 7b29d40834..7bf7f8a2fb 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -1953,14 +1953,11 @@ static int lxc_spawn(struct lxc_handler *handler) } } - ret = lxc_abstract_unix_recv_fds(data_sock1, &handler->conf->devpts_fd, 1, - &handler->conf->devpts_fd, - sizeof(handler->conf->devpts_fd)); + ret = lxc_setup_devpts_parent(handler); if (ret < 0) { SYSERROR("Failed to receive devpts fd from child"); goto out_delete_net; } - TRACE("Received devpts file descriptor %d from child", handler->conf->devpts_fd); /* Now all networks are created, network devices are moved into place, * and the correct names and ifindices in the respective namespaces have From noreply at github.com Tue Oct 20 12:23:07 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Tue, 20 Oct 2020 05:23:07 -0700 Subject: [lxc-devel] [lxc/lxc] 68f389: conf: always send response to parent waiting for d... Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: 68f3899e4aad1695fd31e5da47dcf3897d2ae2d9 https://github.com/lxc/lxc/commit/68f3899e4aad1695fd31e5da47dcf3897d2ae2d9 Author: Christian Brauner Date: 2020-10-20 (Tue, 20 Oct 2020) Changed paths: M src/lxc/conf.c M src/lxc/conf.h M src/lxc/start.c Log Message: ----------- conf: always send response to parent waiting for devptfs_fd When no devpts devices are requested we used to return early but did not send a response to the parent. This is a problem because the parent will be waiting for a devpts fd to be sent. Make sure to always send a response. Signed-off-by: Christian Brauner Commit: f4da1c37e658d4e64c05390acf7a64565b4ebe30 https://github.com/lxc/lxc/commit/f4da1c37e658d4e64c05390acf7a64565b4ebe30 Author: Stéphane Graber Date: 2020-10-20 (Tue, 20 Oct 2020) Changed paths: M src/lxc/conf.c M src/lxc/conf.h M src/lxc/start.c Log Message: ----------- Merge pull request #3558 from brauner/2020-10-20/fixes conf: always send response to parent waiting for devptfs_fd Compare: https://github.com/lxc/lxc/compare/1593efb5d7e8...f4da1c37e658 From builds at travis-ci.org Tue Oct 20 12:42:15 2020 From: builds at travis-ci.org (Travis CI) Date: Tue, 20 Oct 2020 12:42:15 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7900 (master - f4da1c3) In-Reply-To: Message-ID: <5f8edb26e7364_13fed452d441c597ab@travis-tasks-5cd6fb7d67-v8xsl.mail> Build Update for lxc/lxc ------------------------------------- Build: #7900 Status: Errored Duration: 19 mins and 7 secs Commit: f4da1c3 (master) Author: Stéphane Graber Message: Merge pull request #3558 from brauner/2020-10-20/fixes conf: always send response to parent waiting for devptfs_fd View the changeset: https://github.com/lxc/lxc/compare/1593efb5d7e8...f4da1c37e658 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737389972?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lxc-bot at linuxcontainers.org Tue Oct 20 13:18:44 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Tue, 20 Oct 2020 06:18:44 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Instance: Fix a class of bugs related to incorrect log path generation when instance is in non-default project Message-ID: <5f8ee3b4.1c69fb81.53384.aa29SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 674 bytes Desc: not available URL: -------------- next part -------------- From 0364002a9db6bfa9707710b7b64a5ca0a93514a5 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 13:51:58 +0100 Subject: [PATCH 1/5] lxd/instance: Use project aware inst.LogPath() function when clearing log dir in instanceCreateInternal Avoids clearing lxc.conf of a different container and preventing access to it. Signed-off-by: Thomas Parrott --- lxd/instance.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/instance.go b/lxd/instance.go index 2d812f6b97..71718a6390 100644 --- a/lxd/instance.go +++ b/lxd/instance.go @@ -640,15 +640,15 @@ func instanceCreateInternal(s *state.State, args db.InstanceArgs) (instance.Inst s.Cluster.DeleteInstance(dbInst.Project, dbInst.Name) }() - // Wipe any existing log for this instance name. - os.RemoveAll(shared.LogPath(args.Name)) - args = db.InstanceToArgs(&dbInst) inst, err := instance.Create(s, args) if err != nil { return nil, errors.Wrap(err, "Create instance") } + // Wipe any existing log for this instance name. + os.RemoveAll(inst.LogPath()) + revert = false return inst, nil } From c029aab2ca63ea6a83f6f73be8d0e8b7a827a525 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 13:57:21 +0100 Subject: [PATCH 2/5] lxd/instance/drivers/driver/lxc: Project aware rename of log path in Rename() Signed-off-by: Thomas Parrott --- lxd/instance/drivers/driver_lxc.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go index e4290118f5..e99c01f4df 100644 --- a/lxd/instance/drivers/driver_lxc.go +++ b/lxd/instance/drivers/driver_lxc.go @@ -3682,9 +3682,10 @@ func (c *lxc) Rename(newName string) error { } // Rename the logging path. - os.RemoveAll(shared.LogPath(newName)) + newFullName := project.Instance(c.Project(), c.Name()) + os.RemoveAll(shared.LogPath(newFullName)) if shared.PathExists(c.LogPath()) { - err := os.Rename(c.LogPath(), shared.LogPath(newName)) + err := os.Rename(c.LogPath(), shared.LogPath(newFullName)) if err != nil { logger.Error("Failed renaming container", ctxMap) return err From feb558885cbedfbbf55911a5cba96d0890720b74 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 13:58:18 +0100 Subject: [PATCH 3/5] lxd/instance/drivers/driver/qemu: Project aware rename of log path in Rename() Signed-off-by: Thomas Parrott --- lxd/instance/drivers/driver_qemu.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 1abaddbbb2..733a868605 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -2647,9 +2647,10 @@ func (vm *qemu) Rename(newName string) error { } // Rename the logging path. - os.RemoveAll(shared.LogPath(newName)) + newFullName := project.Instance(vm.Project(), vm.Name()) + os.RemoveAll(shared.LogPath(newFullName)) if shared.PathExists(vm.LogPath()) { - err := os.Rename(vm.LogPath(), shared.LogPath(newName)) + err := os.Rename(vm.LogPath(), shared.LogPath(newFullName)) if err != nil { logger.Error("Failed renaming instance", ctxMap) return err From fa7f5e39b6ad4cd27b6e0dd38a3d79d9c147c293 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 14:00:26 +0100 Subject: [PATCH 4/5] lxd/instance/drivers/driver/lxc: Makes collectCRIULogFile project log path aware Signed-off-by: Thomas Parrott --- lxd/instance/drivers/driver_lxc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go index e99c01f4df..81d814e3df 100644 --- a/lxd/instance/drivers/driver_lxc.go +++ b/lxd/instance/drivers/driver_lxc.go @@ -4815,7 +4815,7 @@ func (c *lxc) Export(w io.Writer, properties map[string]string) (api.ImageMetada func collectCRIULogFile(c instance.Instance, imagesDir string, function string, method string) error { t := time.Now().Format(time.RFC3339) - newPath := shared.LogPath(c.Name(), fmt.Sprintf("%s_%s_%s.log", function, method, t)) + newPath := filepath.Join(c.LogPath(), fmt.Sprintf("%s_%s_%s.log", function, method, t)) return shared.FileCopy(filepath.Join(imagesDir, fmt.Sprintf("%s.log", method)), newPath) } From 645ce23fa728eb3922df5466361e6ae0b344c5c4 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 14:11:17 +0100 Subject: [PATCH 5/5] lxd/instance/logs: Makes containerLogsGet project aware Signed-off-by: Thomas Parrott --- lxd/instance_logs.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lxd/instance_logs.go b/lxd/instance_logs.go index 0f7df6893b..c63fb5afde 100644 --- a/lxd/instance_logs.go +++ b/lxd/instance_logs.go @@ -53,11 +53,11 @@ func containerLogsGet(d *Daemon, r *http.Request) response.Response { return response.SmartError(err) } - project := projectParam(r) + projectName := projectParam(r) name := mux.Vars(r)["name"] // Handle requests targeted to a container on a different node - resp, err := forwardedResponseIfInstanceIsRemote(d, r, project, name, instanceType) + resp, err := forwardedResponseIfInstanceIsRemote(d, r, projectName, name, instanceType) if err != nil { return response.SmartError(err) } @@ -72,7 +72,8 @@ func containerLogsGet(d *Daemon, r *http.Request) response.Response { result := []string{} - dents, err := ioutil.ReadDir(shared.LogPath(name)) + fullName := project.Instance(projectName, name) + dents, err := ioutil.ReadDir(shared.LogPath(fullName)) if err != nil { return response.SmartError(err) } From lxc-bot at linuxcontainers.org Tue Oct 20 13:35:11 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Tue, 20 Oct 2020 06:35:11 -0700 (PDT) Subject: [lxc-devel] [lxd/master] init: Clarifies question about using an existing empty disk Message-ID: <5f8ee78f.1c69fb81.2c98d.cb92SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 376 bytes Desc: not available URL: -------------- next part -------------- From 78afd4d6cc3922df13570f863ed7056eb8b71953 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 14:34:06 +0100 Subject: [PATCH] lxd/main/init/interactive: Clarifies question about using an existing empty disk Fixes #8060 Signed-off-by: Thomas Parrott --- lxd/main_init_interactive.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/main_init_interactive.go b/lxd/main_init_interactive.go index 5fa8f755a6..dfc8f0c0de 100644 --- a/lxd/main_init_interactive.go +++ b/lxd/main_init_interactive.go @@ -554,7 +554,7 @@ func (c *cmdInit) askStoragePool(config *cmdInitData, d lxd.InstanceServer, pool // Ask for the name of the cluster pool.Config["source"] = cli.AskString("Name of the CEPHfs volume: ", "", nil) - } else if cli.AskBool("Would you like to use an existing empty disk or partition? (yes/no) [default=no]: ", "no") { + } else if cli.AskBool("Would you like to use an existing empty block device (e.g. a disk or partition)? (yes/no) [default=no]: ", "no") { deviceExists := func(path string) error { if !shared.IsBlockdevPath(path) { return fmt.Errorf("'%s' is not a block device", path) From lxc-bot at linuxcontainers.org Tue Oct 20 14:13:57 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Tue, 20 Oct 2020 07:13:57 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: Sets ipv4.nat=true by default for new fan bridges and adds the setting if missing to existing fan bridges Message-ID: <5f8ef0a5.1c69fb81.eaf1e.9e0eSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 461 bytes Desc: not available URL: -------------- next part -------------- From f6dd88a5b60b99fb65d23f6c769368459366d849 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 14:40:04 +0100 Subject: [PATCH 1/2] lxd/network/driver/bridge: Sets ipv4.nat=true when adding a new fan network with fan.underlay_subnet=auto Signed-off-by: Thomas Parrott --- lxd/network/driver_bridge.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go index e0d8779072..72a0952ca7 100644 --- a/lxd/network/driver_bridge.go +++ b/lxd/network/driver_bridge.go @@ -89,6 +89,10 @@ func (n *bridge) FillConfig(config map[string]string) error { if config["fan.underlay_subnet"] == "" { config["fan.underlay_subnet"] = "auto" } + + if config["fan.underlay_subnet"] == "auto" && config["ipv4.nat"] == "" { + config["ipv4.nat"] = "true" + } } else { if config["ipv4.address"] == "" { config["ipv4.address"] = "auto" From e59f674cee6f2b5c3baeb694d3e351c27726c520 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 15:11:48 +0100 Subject: [PATCH 2/2] lxd/patches: Adds patchNetworkFANEnableNAT to set ipv4.nat=true for fan networks missing the setting Signed-off-by: Thomas Parrott --- lxd/patches.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/lxd/patches.go b/lxd/patches.go index 462cd78173..4b3d415fef 100644 --- a/lxd/patches.go +++ b/lxd/patches.go @@ -103,6 +103,7 @@ var patches = []patch{ {name: "move_backups_instances", stage: patchPostDaemonStorage, run: patchMoveBackupsInstances}, {name: "network_ovn_enable_nat", stage: patchPostDaemonStorage, run: patchNetworkOVNEnableNAT}, {name: "network_ovn_remove_routes", stage: patchPostDaemonStorage, run: patchNetworkOVNRemoveRoutes}, + {name: "network_fan_enable_nat", stage: patchPostDaemonStorage, run: patchNetworkFANEnableNAT}, } type patch struct { @@ -167,6 +168,54 @@ func patchesApply(d *Daemon, stage patchStage) error { // Patches begin here +// patchNetworkFANEnableNAT sets "ipv4.nat=true" on fan bridges that are missing the "ipv4.nat" setting. +// This prevents outbound connectivity breaking on existing fan networks now that the default behaviour of not +// having "ipv4.nat" set is to disable NAT (bringing in line with the non-fan bridge behavior and docs). +func patchNetworkFANEnableNAT(name string, d *Daemon) error { + err := d.cluster.Transaction(func(tx *db.ClusterTx) error { + projectNetworks, err := tx.GetNonPendingNetworks() + if err != nil { + return err + } + + for _, networks := range projectNetworks { + for networkID, network := range networks { + if network.Type != "bridge" { + continue + } + + if network.Config["bridge.mode"] != "fan" { + continue + } + + modified := false + + // Enable ipv4.nat if setting not specified. + if _, found := network.Config["ipv4.nat"]; !found { + modified = true + network.Config["ipv4.nat"] = "true" + } + + if modified { + err = tx.UpdateNetwork(networkID, network.Description, network.Config) + if err != nil { + return errors.Wrapf(err, "Failed setting ipv4.nat=true for fan network %q (%d)", network.Name, networkID) + } + + logger.Debugf("Set ipv4.nat=true for fan network %q (%d)", network.Name, networkID) + } + } + } + + return nil + }) + if err != nil { + return err + } + + return nil +} + // patchNetworkOVNRemoveRoutes removes the "ipv4.routes.external" and "ipv6.routes.external" settings from OVN // networks. It was decided that the OVN NIC level equivalent settings were sufficient. func patchNetworkOVNRemoveRoutes(name string, d *Daemon) error { From lxc-bot at linuxcontainers.org Tue Oct 20 15:41:30 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Tue, 20 Oct 2020 08:41:30 -0700 (PDT) Subject: [lxc-devel] [lxc-ci/master] OVN: External IP tests Message-ID: <5f8f052a.1c69fb81.ce7ec.9f9fSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 303 bytes Desc: not available URL: -------------- next part -------------- From e1759fd36b251872804e9b55e9cbeb9c8b00d57c Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 11:29:28 +0100 Subject: [PATCH 1/4] bin/test-lxd-ovn: Adds debugging throughout script So we can see which commands are being run. Signed-off-by: Thomas Parrott --- bin/test-lxd-ovn | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bin/test-lxd-ovn b/bin/test-lxd-ovn index b6b6810..5ec282f 100755 --- a/bin/test-lxd-ovn +++ b/bin/test-lxd-ovn @@ -1,5 +1,5 @@ #!/bin/sh -set -eu +set -eux cleanup() { echo "" @@ -35,7 +35,6 @@ lxd waitready --timeout=300 apt install ovn-host ovn-central --yes # Configure OVN -set -x ovs-vsctl set open_vswitch . \ external_ids:ovn-remote=unix:/var/run/ovn/ovnsb_db.sock \ external_ids:ovn-encap-type=geneve \ @@ -56,7 +55,6 @@ lxc network create lxdbr0 \ lxc network create ovn-virtual-network --type=ovn # Test -set +x lxc network list lxc project switch default From 8637bc622210597da24e9b4881a27f0ed84aa160 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 16:19:38 +0100 Subject: [PATCH 2/4] bin/test-lxd-ovn: Improves section titles Signed-off-by: Thomas Parrott --- bin/test-lxd-ovn | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/test-lxd-ovn b/bin/test-lxd-ovn index 5ec282f..0ee328f 100755 --- a/bin/test-lxd-ovn +++ b/bin/test-lxd-ovn @@ -98,7 +98,7 @@ echo "==> OVN to OVN" lxc exec u2 -- ping -c1 -4 "${U3_IPV4}" lxc exec u2 -- ping -c1 -6 "${U3_IPV6}" -echo "==> OVN to lxdbr0" +echo "==> OVN to lxdbr0 instance" lxc exec u3 -- ping -c1 -4 "${U1_IPV4}" lxc exec u3 -- ping -c1 -6 "${U1_IPV6}" @@ -106,7 +106,7 @@ echo "==> DNS resolution on OVN" lxc exec u3 -- ping -c1 -4 u2.lxd lxc exec u3 -- ping -c1 -6 u2.lxd -echo "==> OVN to lxdbr0" +echo "==> OVN to lxdbr0 gateway" lxc exec u2 -- ping -c1 10.10.10.1 lxc exec u2 -- ping -c1 fd42:4242:4242:1010::1 @@ -174,7 +174,7 @@ echo "==> OVN to OVN in project testovn" lxc exec u2 -- ping -c1 -4 "${U3_IPV4}" lxc exec u2 -- ping -c1 -6 "${U3_IPV6}" -echo "==> OVN to lxdbr0 in project testovn" +echo "==> OVN to lxdbr0 instance in project testovn" lxc exec u3 -- ping -c1 -4 "${U1_IPV4}" lxc exec u3 -- ping -c1 -6 "${U1_IPV6}" @@ -182,7 +182,7 @@ echo "==> DNS resolution on OVN in project testovn" lxc exec u3 -- ping -c1 -4 u2.lxd lxc exec u3 -- ping -c1 -6 u2.lxd -echo "==> OVN to lxdbr0 in project testovn" +echo "==> OVN to lxdbr0 gateway in project testovn" lxc exec u2 -- ping -c1 10.10.10.1 lxc exec u2 -- ping -c1 fd42:4242:4242:1010::1 From 3a83bc3e415657e66d41880b844b34c97fc0010a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 16:35:15 +0100 Subject: [PATCH 3/4] bin/test-lxd-ovn: Adds test for using external subnet as OVN network address Signed-off-by: Thomas Parrott --- bin/test-lxd-ovn | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/bin/test-lxd-ovn b/bin/test-lxd-ovn index 0ee328f..179fc57 100755 --- a/bin/test-lxd-ovn +++ b/bin/test-lxd-ovn @@ -116,6 +116,7 @@ lxc exec u2 -- ping -c1 -6 linuxcontainers.org echo "===> Testing project restrictions" lxc project create testovn -c features.networks=true -c restricted=true +lxc profile device add default root disk path=/ pool=default --project testovn # Test we cannot create network in restricted project with no defined uplinks. ! lxc network create ovn-virtual-network --project testovn @@ -131,9 +132,45 @@ lxc project set testovn restricted.networks.uplinks=lxdbr0,lxdbr1 ! lxc network create ovn-virtual-network --project testovn lxc network create ovn-virtual-network network=lxdbr0 --project testovn lxc network delete ovn-virtual-network --project testovn -lxc project delete testovn lxc network delete lxdbr1 --project default +# Test physical uplink with external IPs +ip link add dummy0 type dummy +lxc network create dummy --type=physical --project default \ + parent=dummy0 \ + ipv4.gateway=192.0.2.1/24 \ + ipv6.gateway=2001:db8:1:1::1/64 \ + ipv4.ovn.ranges=192.0.2.10-192.0.2.19 \ + ipv4.routes=198.51.100.0/24 \ + ipv6.routes=2001:db8:1:2::/64 \ + dns.nameservers=192.0.2.53 + +# Test using external subnets using physical uplink. +lxc project set testovn restricted.networks.uplinks=dummy +lxc network create ovn-virtual-network --type=ovn --project testovn network=dummy \ + ipv4.address=198.51.100.1/24 \ + ipv6.address=2001:db8:1:2::1/64 \ + ipv4.nat=false \ + ipv6.nat=false + +lxc init images:ubuntu/20.04 u1 --project testovn +lxc config device add u1 eth0 nic network=ovn-virtual-network name=eth0 --project testovn +lxc start u1 --project testovn + +# Test external IPs allocated and published using dnat. +sleep 5 +U1_EXT_IPV4="$(lxc list u1 --project testovn -c4 --format=csv | cut -d' ' -f1)" +U1_EXT_IPV6="$(lxc list u1 --project testovn -c6 --format=csv | cut -d' ' -f1)" +ovn-nbctl --bare --format=csv --column=external_ip,logical_ip,type find nat | grep "${U1_EXT_IPV4},${U1_EXT_IPV4},dnat_and_snat" +ovn-nbctl --bare --format=csv --column=external_ip,logical_ip,type find nat | grep "${U1_EXT_IPV6},${U1_EXT_IPV6},dnat_and_snat" + +lxc delete -f u1 --project testovn +lxc network delete ovn-virtual-network --project testovn +lxc image delete "${FINGERPRINT}" --project testovn +lxc project delete testovn +lxc network delete dummy --project default +ip link delete dummy0 + echo "===> Testing projects" lxc project create testovn -c features.networks=true -c limits.networks=1 lxc project switch testovn From 0e7ba2b5364993593331b70fb499f2de531f2be2 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 20 Oct 2020 16:35:21 +0100 Subject: [PATCH 4/4] bin/test-lxd-ovn: Shorten line length Signed-off-by: Thomas Parrott --- bin/test-lxd-ovn | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/test-lxd-ovn b/bin/test-lxd-ovn index 179fc57..72772b8 100755 --- a/bin/test-lxd-ovn +++ b/bin/test-lxd-ovn @@ -177,7 +177,10 @@ lxc project switch testovn lxc profile device add default root disk path=/ pool=default # Create network inside project with same name and subnet as network in default project. -lxc network create ovn-virtual-network network=lxdbr0 --type=ovn ipv4.address="$(lxc network get ovn-virtual-network ipv4.address --project default)" ipv4.nat=true ipv6.address="$(lxc network get ovn-virtual-network ipv6.address --project default)" ipv6.nat=true +lxc network create ovn-virtual-network network=lxdbr0 --type=ovn \ + ipv4.address="$(lxc network get ovn-virtual-network ipv4.address --project default)" \ + ipv4.nat=true ipv6.address="$(lxc network get ovn-virtual-network ipv6.address --project default)" \ + ipv6.nat=true # Test we cannot exceed specified project limits for networks. ! lxc network create ovn-virtual-network-toomany network=lxdbr0 --type=ovn From lxc-bot at linuxcontainers.org Tue Oct 20 15:42:22 2020 From: lxc-bot at linuxcontainers.org (brauner on Github) Date: Tue, 20 Oct 2020 08:42:22 -0700 (PDT) Subject: [lxc-devel] [lxc/master] conf: account for early return when sending devpts fd Message-ID: <5f8f055e.1c69fb81.c8d83.9d7bSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 364 bytes Desc: not available URL: -------------- next part -------------- From 185b9ee91b6ca601c509fb65993cbecec187029f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 20 Oct 2020 17:41:06 +0200 Subject: [PATCH] conf: account for early return when sending devpts fd Signed-off-by: Christian Brauner --- src/lxc/conf.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index e006ea4391..259d3766ab 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1549,7 +1549,14 @@ static int lxc_setup_devpts_child(struct lxc_handler *handler) if (devpts_fd < 0) { devpts_fd = -EBADF; TRACE("Failed to create detached devpts mount"); + ret = lxc_abstract_unix_send_fds(sock, NULL, 0, &devpts_fd, sizeof(int)); + } else { + ret = lxc_abstract_unix_send_fds(sock, &devpts_fd, 1, NULL, 0); } + if (ret < 0) + return log_error_errno(-1, errno, "Failed to send devpts fd to parent"); + + TRACE("Sent devpts file descriptor %d to parent", devpts_fd); /* Remove any pre-existing /dev/ptmx file. */ ret = remove("/dev/ptmx"); @@ -1583,16 +1590,8 @@ static int lxc_setup_devpts_child(struct lxc_handler *handler) ret = symlink("/dev/pts/ptmx", "/dev/ptmx"); if (ret < 0) return log_error_errno(-1, errno, "Failed to create symlink from \"/dev/ptmx\" to \"/dev/pts/ptmx\""); - DEBUG("Created symlink from \"/dev/ptmx\" to \"/dev/pts/ptmx\""); - if (devpts_fd < 0) - ret = lxc_abstract_unix_send_fds(sock, NULL, 0, &devpts_fd, sizeof(int)); - else - ret = lxc_abstract_unix_send_fds(sock, &devpts_fd, 1, NULL, 0); - if (ret < 0) - return log_error_errno(-1, errno, "Failed to send devpts fd to parent"); - - TRACE("Sent devpts file descriptor %d to parent", devpts_fd); + DEBUG("Created symlink from \"/dev/ptmx\" to \"/dev/pts/ptmx\""); return 0; } From noreply at github.com Tue Oct 20 16:22:14 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Tue, 20 Oct 2020 09:22:14 -0700 Subject: [lxc-devel] [lxc/lxc] 185b9e: conf: account for early return when sending devpts fd Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: 185b9ee91b6ca601c509fb65993cbecec187029f https://github.com/lxc/lxc/commit/185b9ee91b6ca601c509fb65993cbecec187029f Author: Christian Brauner Date: 2020-10-20 (Tue, 20 Oct 2020) Changed paths: M src/lxc/conf.c Log Message: ----------- conf: account for early return when sending devpts fd Signed-off-by: Christian Brauner Commit: c639f45ee56b6e7ef46e9492642c18569a06a9a5 https://github.com/lxc/lxc/commit/c639f45ee56b6e7ef46e9492642c18569a06a9a5 Author: Stéphane Graber Date: 2020-10-20 (Tue, 20 Oct 2020) Changed paths: M src/lxc/conf.c Log Message: ----------- Merge pull request #3559 from brauner/2020-10-20/fixes conf: account for early return when sending devpts fd Compare: https://github.com/lxc/lxc/compare/f4da1c37e658...c639f45ee56b From builds at travis-ci.org Tue Oct 20 16:41:05 2020 From: builds at travis-ci.org (Travis CI) Date: Tue, 20 Oct 2020 16:41:05 +0000 Subject: [lxc-devel] Passed: lxc/lxc#7902 (master - c639f45) In-Reply-To: Message-ID: <5f8f132167cae_13ffd002c2f1c49619@travis-tasks-6fc4cd69d9-rht9d.mail> Build Update for lxc/lxc ------------------------------------- Build: #7902 Status: Passed Duration: 6 mins and 5 secs Commit: c639f45 (master) Author: Stéphane Graber Message: Merge pull request #3559 from brauner/2020-10-20/fixes conf: account for early return when sending devpts fd View the changeset: https://github.com/lxc/lxc/compare/f4da1c37e658...c639f45ee56b View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737465367?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lxc-bot at linuxcontainers.org Tue Oct 20 17:59:23 2020 From: lxc-bot at linuxcontainers.org (dlemel8 on Github) Date: Tue, 20 Oct 2020 10:59:23 -0700 (PDT) Subject: [lxc-devel] [lxd/master] add new "restarted" event to reboot section of onStop in both lxc and qemu Message-ID: <5f8f257b.1c69fb81.5dfe2.b057SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 39eb834ea368507e2f1dbfab7b0ef156ce7bc60d Mon Sep 17 00:00:00 2001 From: Daniel Segal Date: Tue, 20 Oct 2020 20:55:59 +0300 Subject: [PATCH] add new "restarted" event to reboot section of onStop in both lxc and qemu --- lxd/instance/drivers/driver_lxc.go | 2 ++ lxd/instance/drivers/driver_qemu.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go index ac307d6d1e..ccb8945587 100644 --- a/lxd/instance/drivers/driver_lxc.go +++ b/lxd/instance/drivers/driver_lxc.go @@ -2914,6 +2914,8 @@ func (c *lxc) onStop(args map[string]string) error { if target == "reboot" { // Start the container again err = c.Start(false) + c.state.Events.SendLifecycle(c.project, "container-restarted", + fmt.Sprintf("/1.0/containers/%s", c.name), nil) return } diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 5aebe9509c..d1436b9263 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -545,6 +545,8 @@ func (vm *qemu) onStop(target string) error { if target == "reboot" { err = vm.Start(false) + vm.state.Events.SendLifecycle(vm.project, "virtual-machine-restarted", + fmt.Sprintf("/1.0/virtual-machines/%s", vm.name), nil) } else if vm.ephemeral { // Destroy ephemeral virtual machines err = vm.Delete() From lxc-bot at linuxcontainers.org Tue Oct 20 19:05:07 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Tue, 20 Oct 2020 12:05:07 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Disable USB on s390x Message-ID: <5f8f34e3.1c69fb81.e0cee.e102SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 524c519eefe8a87b6152e152f04e1755fc0d1fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 20 Oct 2020 14:35:03 -0400 Subject: [PATCH 1/3] lxd/seccomp: Fix go vet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/seccomp/seccomp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/seccomp/seccomp.go b/lxd/seccomp/seccomp.go index 3959fab4b4..90be41223e 100644 --- a/lxd/seccomp/seccomp.go +++ b/lxd/seccomp/seccomp.go @@ -2026,7 +2026,7 @@ func (s *Server) MountSyscallShift(c Instance) bool { return false } -var pageSize int = 4096 +var pageSize = 4096 func init() { tmp := unix.Getpagesize() From a4f287312fb03f3b3d58201fa9e7ec568d5e7176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 20 Oct 2020 14:57:22 -0400 Subject: [PATCH 2/3] lxd/instance: Add Architecture to common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/instance/drivers/driver_common.go | 6 ++++++ lxd/instance/drivers/driver_lxc.go | 10 ++-------- lxd/instance/drivers/driver_qemu.go | 10 ++-------- lxd/instance/instance_interface.go | 2 +- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/lxd/instance/drivers/driver_common.go b/lxd/instance/drivers/driver_common.go index 7f3e9da6dd..05cc85be09 100644 --- a/lxd/instance/drivers/driver_common.go +++ b/lxd/instance/drivers/driver_common.go @@ -11,6 +11,7 @@ import ( // common provides structure common to all instance types. type common struct { dbType instancetype.Type + architecture int devPaths []string expandedConfig map[string]string expandedDevices deviceConfig.Devices @@ -31,6 +32,11 @@ func (c *common) Type() instancetype.Type { return c.dbType } +// Architecture returns the instance's architecture. +func (c *common) Architecture() int { + return c.architecture +} + // ExpandedConfig returns instance's expanded config. func (c *common) ExpandedConfig() map[string]string { return c.expandedConfig diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go index 81d814e3df..c5b694e0ee 100644 --- a/lxd/instance/drivers/driver_lxc.go +++ b/lxd/instance/drivers/driver_lxc.go @@ -145,6 +145,7 @@ func lxcCreate(s *state.State, args db.InstanceArgs) (instance.Instance, error) c := &lxc{ common: common{ dbType: args.Type, + architecture: args.Architecture, localConfig: args.Config, localDevices: args.Devices, project: args.Project, @@ -156,7 +157,6 @@ func lxcCreate(s *state.State, args db.InstanceArgs) (instance.Instance, error) node: args.Node, description: args.Description, ephemeral: args.Ephemeral, - architecture: args.Architecture, snapshot: args.Snapshot, stateful: args.Stateful, creationDate: args.CreationDate, @@ -403,6 +403,7 @@ func lxcInstantiate(s *state.State, args db.InstanceArgs, expandedDevices device c := &lxc{ common: common{ dbType: args.Type, + architecture: args.Architecture, localConfig: args.Config, localDevices: args.Devices, project: args.Project, @@ -413,7 +414,6 @@ func lxcInstantiate(s *state.State, args db.InstanceArgs, expandedDevices device name: args.Name, description: args.Description, ephemeral: args.Ephemeral, - architecture: args.Architecture, snapshot: args.Snapshot, creationDate: args.CreationDate, lastUsedDate: args.LastUsedDate, @@ -448,7 +448,6 @@ type lxc struct { common // Properties - architecture int snapshot bool creationDate time.Time lastUsedDate time.Time @@ -6638,11 +6637,6 @@ func (c *lxc) IsSnapshot() bool { return c.snapshot } -// Architecture returns architecture of instance. -func (c *lxc) Architecture() int { - return c.architecture -} - // CreationDate returns creation date of instance. func (c *lxc) CreationDate() time.Time { return c.creationDate diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 733a868605..9f433b2112 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -103,6 +103,7 @@ func qemuInstantiate(s *state.State, args db.InstanceArgs, expandedDevices devic vm := &qemu{ common: common{ dbType: args.Type, + architecture: args.Architecture, localConfig: args.Config, localDevices: args.Devices, project: args.Project, @@ -113,7 +114,6 @@ func qemuInstantiate(s *state.State, args db.InstanceArgs, expandedDevices devic name: args.Name, description: args.Description, ephemeral: args.Ephemeral, - architecture: args.Architecture, snapshot: args.Snapshot, creationDate: args.CreationDate, lastUsedDate: args.LastUsedDate, @@ -155,6 +155,7 @@ func qemuCreate(s *state.State, args db.InstanceArgs) (instance.Instance, error) vm := &qemu{ common: common{ dbType: args.Type, + architecture: args.Architecture, localConfig: args.Config, localDevices: args.Devices, state: s, @@ -166,7 +167,6 @@ func qemuCreate(s *state.State, args db.InstanceArgs) (instance.Instance, error) node: args.Node, description: args.Description, ephemeral: args.Ephemeral, - architecture: args.Architecture, snapshot: args.Snapshot, stateful: args.Stateful, creationDate: args.CreationDate, @@ -312,7 +312,6 @@ type qemu struct { common // Properties. - architecture int snapshot bool creationDate time.Time lastUsedDate time.Time @@ -4367,11 +4366,6 @@ func (vm *qemu) Description() string { return vm.description } -// Architecture returns the instance's architecture. -func (vm *qemu) Architecture() int { - return vm.architecture -} - // CreationDate returns the instance's creation date. func (vm *qemu) CreationDate() time.Time { return vm.creationDate diff --git a/lxd/instance/instance_interface.go b/lxd/instance/instance_interface.go index e49458bd86..a19cdccba8 100644 --- a/lxd/instance/instance_interface.go +++ b/lxd/instance/instance_interface.go @@ -35,6 +35,7 @@ const ( type ConfigReader interface { Project() string Type() instancetype.Type + Architecture() int ExpandedConfig() map[string]string ExpandedDevices() deviceConfig.Devices LocalConfig() map[string]string @@ -105,7 +106,6 @@ type Instance interface { Location() string Name() string Description() string - Architecture() int CreationDate() time.Time LastUsedDate() time.Time From fce5ee2821b109a013c91fe8b2a64c6b89c92d72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 20 Oct 2020 14:57:50 -0400 Subject: [PATCH 3/3] lxd/devices: Disable USB on s390x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/device/usb.go | 5 +++++ lxd/instance/drivers/driver_qemu.go | 21 ++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lxd/device/usb.go b/lxd/device/usb.go index 4c6efea101..e668acf278 100644 --- a/lxd/device/usb.go +++ b/lxd/device/usb.go @@ -11,6 +11,7 @@ import ( "github.com/lxc/lxd/lxd/instance" "github.com/lxc/lxd/lxd/instance/instancetype" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/osarch" "github.com/lxc/lxd/shared/validate" ) @@ -49,6 +50,10 @@ func (d *usb) validateConfig(instConf instance.ConfigReader) error { return ErrUnsupportedDevType } + if instConf.Architecture() == osarch.ARCH_64BIT_S390_BIG_ENDIAN { + return fmt.Errorf("USB devices aren't supported on s390x") + } + rules := map[string]func(string) error{ "vendorid": validate.Optional(validate.IsDeviceID), "productid": validate.Optional(validate.IsDeviceID), diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 9f433b2112..b1932e5fad 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -1755,15 +1755,18 @@ func (vm *qemu) generateQemuConfigFile(busName string, devConfs []*deviceConfig. return "", err } - devBus, devAddr, multi = bus.allocate(busFunctionGroupGeneric) - err = qemuUSB.Execute(sb, map[string]interface{}{ - "bus": bus.name, - "devBus": devBus, - "devAddr": devAddr, - "multifunction": multi, - }) - if err != nil { - return "", err + // s390x doesn't really have USB. + if vm.architecture != osarch.ARCH_64BIT_S390_BIG_ENDIAN { + devBus, devAddr, multi = bus.allocate(busFunctionGroupGeneric) + err = qemuUSB.Execute(sb, map[string]interface{}{ + "bus": bus.name, + "devBus": devBus, + "devAddr": devAddr, + "multifunction": multi, + }) + if err != nil { + return "", err + } } devBus, devAddr, multi = bus.allocate(busFunctionGroupNone) From lxc-bot at linuxcontainers.org Tue Oct 20 19:43:41 2020 From: lxc-bot at linuxcontainers.org (dlemel8 on Github) Date: Tue, 20 Oct 2020 12:43:41 -0700 (PDT) Subject: [lxc-devel] [lxd/master] add new "restarted" event to reboot section of onStop in both lxc and qemu Message-ID: <5f8f3ded.1c69fb81.5c9e4.02e8SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 95d2afa5f5ba37e635a8236f5f8775c438fd6da8 Mon Sep 17 00:00:00 2001 From: Daniel Segal Date: Tue, 20 Oct 2020 22:42:06 +0300 Subject: [PATCH] add new "restarted" event to reboot section of onStop in both lxc and qemu Signed-off-by: Daniel Segal --- lxd/instance/drivers/driver_lxc.go | 2 ++ lxd/instance/drivers/driver_qemu.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go index ac307d6d1e..ccb8945587 100644 --- a/lxd/instance/drivers/driver_lxc.go +++ b/lxd/instance/drivers/driver_lxc.go @@ -2914,6 +2914,8 @@ func (c *lxc) onStop(args map[string]string) error { if target == "reboot" { // Start the container again err = c.Start(false) + c.state.Events.SendLifecycle(c.project, "container-restarted", + fmt.Sprintf("/1.0/containers/%s", c.name), nil) return } diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 5aebe9509c..d1436b9263 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -545,6 +545,8 @@ func (vm *qemu) onStop(target string) error { if target == "reboot" { err = vm.Start(false) + vm.state.Events.SendLifecycle(vm.project, "virtual-machine-restarted", + fmt.Sprintf("/1.0/virtual-machines/%s", vm.name), nil) } else if vm.ephemeral { // Destroy ephemeral virtual machines err = vm.Delete() From lxc-bot at linuxcontainers.org Tue Oct 20 22:42:49 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Tue, 20 Oct 2020 15:42:49 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Fix storage leaks in tests Message-ID: <5f8f67e9.1c69fb81.49ca3.5ff8SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 027ed7dbad97446eec5c9d93c6754bd8635f7c2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 20 Oct 2020 17:27:06 -0400 Subject: [PATCH 1/3] tests: Fix missing clustering cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- test/suites/clustering.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/suites/clustering.sh b/test/suites/clustering.sh index 1ce7d1781b..0157dbbb2d 100644 --- a/test/suites/clustering.sh +++ b/test/suites/clustering.sh @@ -689,6 +689,8 @@ test_clustering_storage_single_node() { # Delete the storage pool LXD_DIR="${LXD_ONE_DIR}" lxc storage delete pool1 + printf 'config: {}\ndevices: {}' | LXD_DIR="${LXD_ONE_DIR}" lxc profile edit default + LXD_DIR="${LXD_ONE_DIR}" lxc storage delete data LXD_DIR="${LXD_ONE_DIR}" lxd shutdown sleep 0.5 rm -f "${LXD_ONE_DIR}/unix.socket" From 39e66f7095c313d45cde5c770304f23ec7e034ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 20 Oct 2020 17:47:50 -0400 Subject: [PATCH 2/3] lxd/storage/zfs: Properly recurse delete volumes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/storage/drivers/driver_zfs_utils.go | 45 +++++++++++++++++++++++ lxd/storage/drivers/driver_zfs_volumes.go | 37 +------------------ 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/lxd/storage/drivers/driver_zfs_utils.go b/lxd/storage/drivers/driver_zfs_utils.go index edc01f2dbb..13e59cb80f 100644 --- a/lxd/storage/drivers/driver_zfs_utils.go +++ b/lxd/storage/drivers/driver_zfs_utils.go @@ -81,6 +81,51 @@ func (d *zfs) checkDataset(dataset string) bool { return strings.TrimSpace(out) == dataset } +func (d *zfs) deleteDatasetRecursive(dataset string) error { + // Locate the origin snapshot (if any). + origin, err := d.getDatasetProperty(dataset, "origin") + if err != nil { + return err + } + + // Delete the dataset (and any snapshots left). + _, err = shared.RunCommand("zfs", "destroy", "-r", dataset) + if err != nil { + return err + } + + // Check if the origin can now be deleted. + if origin != "" && origin != "-" { + if strings.HasPrefix(origin, filepath.Join(d.config["zfs.pool_name"], "deleted")) { + // Strip the snapshot name when dealing with a deleted volume. + dataset = strings.SplitN(origin, "@", 2)[0] + } else if strings.Contains(origin, "@deleted-") || strings.Contains(origin, "@copy-") { + // Handle deleted snapshots. + dataset = origin + } else { + // Origin is still active. + dataset = "" + } + + if dataset != "" { + // Get all clones. + clones, err := d.getClones(dataset) + if err != nil { + return err + } + + if len(clones) == 0 { + // Delete the origin. + err = d.deleteDatasetRecursive(dataset) + if err != nil { + return err + } + } + } + } + return nil +} + func (d *zfs) getClones(dataset string) ([]string, error) { out, err := shared.RunCommand("zfs", "get", "-H", "-p", "-r", "-o", "value", "clones", dataset) if err != nil { diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go index 57103887cf..0f06215306 100644 --- a/lxd/storage/drivers/driver_zfs_volumes.go +++ b/lxd/storage/drivers/driver_zfs_volumes.go @@ -736,45 +736,10 @@ func (d *zfs) DeleteVolume(vol Volume, op *operations.Operation) error { return err } } else { - // Locate the origin snapshot (if any). - origin, err := d.getDatasetProperty(d.dataset(vol, false), "origin") + err := d.deleteDatasetRecursive(d.dataset(vol, false)) if err != nil { return err } - - // Delete the dataset (and any snapshots left). - _, err = shared.RunCommand("zfs", "destroy", "-r", d.dataset(vol, false)) - if err != nil { - return err - } - - // Check if the origin can now be deleted. - if origin != "" && origin != "-" { - dataset := "" - if strings.HasPrefix(origin, filepath.Join(d.config["zfs.pool_name"], "deleted")) { - // Strip the snapshot name when dealing with a deleted volume. - dataset = strings.SplitN(origin, "@", 2)[0] - } else if strings.Contains(origin, "@deleted-") || strings.Contains(origin, "@copy-") { - // Handle deleted snapshots. - dataset = origin - } - - if dataset != "" { - // Get all clones. - clones, err := d.getClones(dataset) - if err != nil { - return err - } - - if len(clones) == 0 { - // Delete the origin. - _, err := shared.RunCommand("zfs", "destroy", "-r", dataset) - if err != nil { - return err - } - } - } - } } } From a4bc53da158f25e442f6c165c78ca3bf77494ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 20 Oct 2020 18:37:53 -0400 Subject: [PATCH 3/3] tests: Fix cleanup in backup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- test/suites/backup.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/suites/backup.sh b/test/suites/backup.sh index 11b79c10cd..0392aca437 100644 --- a/test/suites/backup.sh +++ b/test/suites/backup.sh @@ -622,9 +622,11 @@ test_backup_volume_export_with_project() { rmdir "${LXD_DIR}/non-optimized" if [ "$#" -ne 0 ]; then - lxc image rm testimage lxc project switch default + lxc image rm testimage --project "$project" + lxc image rm testimage --project "$project-b" lxc project delete "$project" + lxc project delete "$project-b" fi } From lxc-bot at linuxcontainers.org Wed Oct 21 10:28:48 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Wed, 21 Oct 2020 03:28:48 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Instance: Mount instance volume before devices start Message-ID: <5f900d60.1c69fb81.61f58.4bf0SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 417 bytes Desc: not available URL: -------------- next part -------------- From 6b3d569c493e7cc5e0367fb45d4f6a54c60fe636 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:14:56 +0100 Subject: [PATCH 01/18] lxd/storage/backend/lxd: b.driver.UnmountVolume usage Signed-off-by: Thomas Parrott --- lxd/storage/backend_lxd.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index 20ddfc145c..e14f911692 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -1652,7 +1652,7 @@ func (b *lxdBackend) UnmountInstance(inst instance.Instance, op *operations.Oper // Get the volume. vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf) - return b.driver.UnmountVolume(vol, op) + return b.driver.UnmountVolume(vol, false, op) } // GetInstanceDisk returns the location of the disk. @@ -2976,7 +2976,7 @@ func (b *lxdBackend) UnmountCustomVolume(projectName, volName string, op *operat volStorageName := project.StorageVolume(projectName, volName) vol := b.newVolume(drivers.VolumeTypeCustom, drivers.ContentTypeFS, volStorageName, volume.Config) - return b.driver.UnmountVolume(vol, op) + return b.driver.UnmountVolume(vol, false, op) } // CreateCustomVolumeSnapshot creates a snapshot of a custom volume. From 4c1aa33f61f9a4bb6bc757629d6f99c3eea61b92 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:15:26 +0100 Subject: [PATCH 02/18] lxd/instance/drivers/driver/lxc: Moves log rotate and mount before devices start in startCommon This is to accomodate TPM devices that need to write their state to the instance's volume during start. Signed-off-by: Thomas Parrott --- lxd/instance/drivers/driver_lxc.go | 32 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go index 03170057c3..aa3228cdb0 100644 --- a/lxd/instance/drivers/driver_lxc.go +++ b/lxd/instance/drivers/driver_lxc.go @@ -2077,6 +2077,22 @@ func (c *lxc) startCommon() (string, []func() error, error) { return "", postStartHooks, err } + // Rotate the log file. + logfile := c.LogFilePath() + if shared.PathExists(logfile) { + os.Remove(logfile + ".old") + err := os.Rename(logfile, logfile+".old") + if err != nil { + return "", postStartHooks, err + } + } + + // Mount instance root volume. + ourStart, err = c.mount() + if err != nil { + return "", postStartHooks, err + } + // Create the devices nicID := -1 @@ -2224,22 +2240,6 @@ func (c *lxc) startCommon() (string, []func() error, error) { } } - // Rotate the log file - logfile := c.LogFilePath() - if shared.PathExists(logfile) { - os.Remove(logfile + ".old") - err := os.Rename(logfile, logfile+".old") - if err != nil { - return "", postStartHooks, err - } - } - - // Storage is guaranteed to be mountable now (must be called after devices setup). - ourStart, err = c.mount() - if err != nil { - return "", postStartHooks, err - } - // Generate the LXC config configPath := filepath.Join(c.LogPath(), "lxc.conf") err = c.c.SaveConfigFile(configPath) From 65ee12cf2442b95f998e32b3221bee15706cb6b1 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:16:09 +0100 Subject: [PATCH 03/18] lxd/storage/drivers/interface: Adds keepBlockDev arg to UnmountVolume Signed-off-by: Thomas Parrott --- lxd/storage/drivers/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage/drivers/interface.go b/lxd/storage/drivers/interface.go index 4aba1a134c..7913b221e1 100644 --- a/lxd/storage/drivers/interface.go +++ b/lxd/storage/drivers/interface.go @@ -67,7 +67,7 @@ type Driver interface { // UnmountVolume unmounts a storage volume, returns true if unmounted, false if was not // mounted. - UnmountVolume(vol Volume, op *operations.Operation) (bool, error) + UnmountVolume(vol Volume, keepBlockDev bool, op *operations.Operation) (bool, error) // UnmountVolume unmounts a storage volume snapshot, returns true if unmounted, false if was // not mounted. From 9eb284da66d53e7a73188a6c9c3d66b314df94cb Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:16:45 +0100 Subject: [PATCH 04/18] lxf/storage/drivers/volume: v.driver.UnmountVolume usage Signed-off-by: Thomas Parrott --- lxd/storage/drivers/volume.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage/drivers/volume.go b/lxd/storage/drivers/volume.go index d5678e0b2f..47b977496f 100644 --- a/lxd/storage/drivers/volume.go +++ b/lxd/storage/drivers/volume.go @@ -218,7 +218,7 @@ func (v Volume) MountTask(task func(mountPath string, op *operations.Operation) if ourMount { defer func() { unlock := locking.Lock(OperationLockName(v.pool, string(v.volType), v.name)) - v.driver.UnmountVolume(v, op) + v.driver.UnmountVolume(v, false, op) unlock() }() } From ab34fe525fd394e4b3934970b8f949aedfafd863 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:17:05 +0100 Subject: [PATCH 05/18] lxd/storage/drivers/volume: Adds keepBlockDev arg to UnmountTask Signed-off-by: Thomas Parrott --- lxd/storage/drivers/volume.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lxd/storage/drivers/volume.go b/lxd/storage/drivers/volume.go index 47b977496f..9761fc9054 100644 --- a/lxd/storage/drivers/volume.go +++ b/lxd/storage/drivers/volume.go @@ -227,9 +227,10 @@ func (v Volume) MountTask(task func(mountPath string, op *operations.Operation) return task(v.MountPath(), op) } -// UnmountTask runs the supplied task after unmounting the volume if needed. If the volume was unmounted -// for this then it is mounted when the task finishes. -func (v Volume) UnmountTask(task func(op *operations.Operation) error, op *operations.Operation) error { +// UnmountTask runs the supplied task after unmounting the volume if needed. +// If the volume was unmounted for this then it is mounted when the task finishes. +// keepBlockDev indicates if backing block device should be not be deactivated if volume is unmounted. +func (v Volume) UnmountTask(task func(op *operations.Operation) error, keepBlockDev bool, op *operations.Operation) error { // If the volume is a snapshot then call the snapshot specific mount/unmount functions as // these will mount the snapshot read only. if v.IsSnapshot() { @@ -253,7 +254,7 @@ func (v Volume) UnmountTask(task func(op *operations.Operation) error, op *opera } else { unlock := locking.Lock(OperationLockName(v.pool, string(v.volType), v.name)) - ourUnmount, err := v.driver.UnmountVolume(v, op) + ourUnmount, err := v.driver.UnmountVolume(v, keepBlockDev, op) if err != nil { unlock() return err From 54472ad66be90990d8a23dfe07c3256ec873dd98 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:17:33 +0100 Subject: [PATCH 06/18] lxd/storage/drivers/utils: Passes true for keepBlockDev arg to UnmounTask in shrinkFileSystem Allows for a block backed storage backend to keep its block device activate to allow shrink of filesystem if filesystem was already mounted. Signed-off-by: Thomas Parrott --- lxd/storage/drivers/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage/drivers/utils.go b/lxd/storage/drivers/utils.go index 437efda622..de985134bb 100644 --- a/lxd/storage/drivers/utils.go +++ b/lxd/storage/drivers/utils.go @@ -505,7 +505,7 @@ func shrinkFileSystem(fsType string, devPath string, vol Volume, byteSize int64) } return nil - }, nil) + }, true, nil) case "btrfs": return vol.MountTask(func(mountPath string, op *operations.Operation) error { _, err := shared.RunCommand("btrfs", "filesystem", "resize", strSize, mountPath) From d1144f577bd22587693ebf1c1c6754c15882d863 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:18:47 +0100 Subject: [PATCH 07/18] lxd/storage/drivers/generic/vfs: d.UnmountVolume usage Signed-off-by: Thomas Parrott --- lxd/storage/drivers/generic_vfs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/storage/drivers/generic_vfs.go b/lxd/storage/drivers/generic_vfs.go index 8e36f6247a..bd7ace84a4 100644 --- a/lxd/storage/drivers/generic_vfs.go +++ b/lxd/storage/drivers/generic_vfs.go @@ -816,7 +816,7 @@ func genericVFSBackupUnpack(d Driver, vol Volume, snapshots []string, srcData io // backup restore process to unmount the volume if needed. postHook = func(vol Volume) error { if ourMount { - d.UnmountVolume(vol, op) + d.UnmountVolume(vol, false, op) } return nil @@ -824,7 +824,7 @@ func genericVFSBackupUnpack(d Driver, vol Volume, snapshots []string, srcData io } else { // For custom volumes unmount now, there is no post hook as there is no backup.yaml to generate. if ourMount { - d.UnmountVolume(vol, op) + d.UnmountVolume(vol, false, op) } } From ed358c62fc7cdcfa6cbb6ed03f1858cfe6553a48 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:19:03 +0100 Subject: [PATCH 08/18] lxd/storage/drivers/drivers/mock: Adds keepBlockDev arg to UnmountVolume Signed-off-by: Thomas Parrott --- lxd/storage/drivers/drivers_mock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage/drivers/drivers_mock.go b/lxd/storage/drivers/drivers_mock.go index 9d12ac3dee..bc876b075d 100644 --- a/lxd/storage/drivers/drivers_mock.go +++ b/lxd/storage/drivers/drivers_mock.go @@ -149,7 +149,7 @@ func (d *mock) MountVolume(vol Volume, op *operations.Operation) (bool, error) { // UnmountVolume simulates unmounting a volume. As dir driver doesn't have volumes to unmount it // returns false indicating the volume was already unmounted. -func (d *mock) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) { +func (d *mock) UnmountVolume(vol Volume, keepBlockDev bool, op *operations.Operation) (bool, error) { return false, nil } From 0abd8e94fbc22b5a1d5f9a2e899960da90d8ec81 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:19:43 +0100 Subject: [PATCH 09/18] lxd/storage/drivers/driver/btrfs/volumes: Adds keepBlockDev arg to UnmountVolume Signed-off-by: Thomas Parrott --- lxd/storage/drivers/driver_btrfs_volumes.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go index d2a99e5640..c9bafdef88 100644 --- a/lxd/storage/drivers/driver_btrfs_volumes.go +++ b/lxd/storage/drivers/driver_btrfs_volumes.go @@ -771,7 +771,8 @@ func (d *btrfs) MountVolume(vol Volume, op *operations.Operation) (bool, error) } // UnmountVolume simulates unmounting a volume. -func (d *btrfs) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) { +// As driver doesn't have volumes to unmount it returns false indicating the volume was already unmounted. +func (d *btrfs) UnmountVolume(vol Volume, keepBlockDev bool, op *operations.Operation) (bool, error) { return false, nil } From 7d150e4898b4137af5d49776b653d15db8322f4a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:20:02 +0100 Subject: [PATCH 10/18] lxd/storage/drivers/driver/dir/volumes: Adds keepBlockDev arg to UnmountVolume Signed-off-by: Thomas Parrott --- lxd/storage/drivers/driver_dir_volumes.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/storage/drivers/driver_dir_volumes.go b/lxd/storage/drivers/driver_dir_volumes.go index 2cf3f63c42..a189f908ed 100644 --- a/lxd/storage/drivers/driver_dir_volumes.go +++ b/lxd/storage/drivers/driver_dir_volumes.go @@ -322,9 +322,9 @@ func (d *dir) MountVolume(vol Volume, op *operations.Operation) (bool, error) { return false, nil } -// UnmountVolume simulates unmounting a volume. As dir driver doesn't have volumes to unmount it -// returns false indicating the volume was already unmounted. -func (d *dir) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) { +// UnmountVolume simulates unmounting a volume. +// As driver doesn't have volumes to unmount it returns false indicating the volume was already unmounted. +func (d *dir) UnmountVolume(vol Volume, keepBlockDev bool, op *operations.Operation) (bool, error) { return false, nil } From 0049a9745be5ad1b72e6bd24bc3aeb8c1218c451 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:20:18 +0100 Subject: [PATCH 11/18] lxd/storage/drivers/driver/cephfs/volumes: Adds keepBlockDev arg to UnmountVolume Signed-off-by: Thomas Parrott --- lxd/storage/drivers/driver_cephfs_volumes.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lxd/storage/drivers/driver_cephfs_volumes.go b/lxd/storage/drivers/driver_cephfs_volumes.go index bdde9bfad1..af9058d5d8 100644 --- a/lxd/storage/drivers/driver_cephfs_volumes.go +++ b/lxd/storage/drivers/driver_cephfs_volumes.go @@ -343,7 +343,8 @@ func (d *cephfs) MountVolume(vol Volume, op *operations.Operation) (bool, error) } // UnmountVolume clears any runtime state for the volume. -func (d *cephfs) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) { +// As driver doesn't have volumes to unmount it returns false indicating the volume was already unmounted. +func (d *cephfs) UnmountVolume(vol Volume, keepBlockDev bool, op *operations.Operation) (bool, error) { return false, nil } From 010ec64e97dd9e3d60d99cbe2f21d34277c624a3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:21:26 +0100 Subject: [PATCH 12/18] lxd/storage/drivers/driver/lvm/volumes: UnmountVolume usage Signed-off-by: Thomas Parrott --- lxd/storage/drivers/driver_lvm_volumes.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lxd/storage/drivers/driver_lvm_volumes.go b/lxd/storage/drivers/driver_lvm_volumes.go index e761fa2582..3a763f9448 100644 --- a/lxd/storage/drivers/driver_lvm_volumes.go +++ b/lxd/storage/drivers/driver_lvm_volumes.go @@ -188,7 +188,7 @@ func (d *lvm) DeleteVolume(vol Volume, op *operations.Operation) error { if lvExists { if vol.contentType == ContentTypeFS { - _, err = d.UnmountVolume(vol, op) + _, err = d.UnmountVolume(vol, false, op) if err != nil { return errors.Wrapf(err, "Error unmounting LVM logical volume") } @@ -502,7 +502,7 @@ func (d *lvm) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) // For VMs, unmount the filesystem volume. if vol.IsVMBlock() { fsVol := vol.NewVMBlockFilesystemVolume() - return d.UnmountVolume(fsVol, op) + return d.UnmountVolume(fsVol, false, op) } return deactivated, nil @@ -661,7 +661,7 @@ func (d *lvm) DeleteVolumeSnapshot(snapVol Volume, op *operations.Operation) err } if lvExists { - _, err = d.UnmountVolume(snapVol, op) + _, err = d.UnmountVolume(snapVol, false, op) if err != nil { return errors.Wrapf(err, "Error unmounting LVM logical volume") } @@ -923,7 +923,7 @@ func (d *lvm) RestoreVolume(vol Volume, snapshotName string, op *operations.Oper // 2. Create a writable snapshot with the original name from the snapshot being restored. // 3. Delete the renamed original volume. if d.usesThinpool() { - _, err = d.UnmountVolume(vol, op) + _, err = d.UnmountVolume(vol, false, op) if err != nil { return errors.Wrapf(err, "Error unmounting LVM logical volume") } From 530655d6eb70a61fc6914e84b1431d819f1bebd6 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:21:49 +0100 Subject: [PATCH 13/18] lxd/storage/drivers/driver/lvm/volumes: Adds keepBlockDev arg to UnmountVolume Signed-off-by: Thomas Parrott --- lxd/storage/drivers/driver_lvm_volumes.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lxd/storage/drivers/driver_lvm_volumes.go b/lxd/storage/drivers/driver_lvm_volumes.go index 3a763f9448..614e3b215b 100644 --- a/lxd/storage/drivers/driver_lvm_volumes.go +++ b/lxd/storage/drivers/driver_lvm_volumes.go @@ -468,7 +468,8 @@ func (d *lvm) MountVolume(vol Volume, op *operations.Operation) (bool, error) { } // UnmountVolume unmounts a volume. Returns true if we unmounted. -func (d *lvm) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) { +// keepBlockDev indicates if backing block device should be not be deactivated if volume is unmounted. +func (d *lvm) UnmountVolume(vol Volume, keepBlockDev bool, op *operations.Operation) (bool, error) { var err error volDevPath := d.lvmDevPath(d.config["lvm.vg_name"], vol.volType, vol.contentType, vol.name) mountPath := vol.MountPath() @@ -479,20 +480,22 @@ func (d *lvm) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) if err != nil { return false, errors.Wrapf(err, "Failed to unmount LVM logical volume") } - d.logger.Debug("Unmounted logical volume", log.Ctx{"path": mountPath}) + d.logger.Debug("Unmounted logical volume", log.Ctx{"path": mountPath, "keepBlockDev": keepBlockDev}) // We only deactivate filesystem volumes if an unmount was needed to better align with our // unmount return value indicator. - _, err = d.deactivateVolume(volDevPath) - if err != nil { - return false, err + if !keepBlockDev { + _, err = d.deactivateVolume(volDevPath) + if err != nil { + return false, err + } } return true, nil } deactivated := false - if vol.contentType == ContentTypeBlock { + if vol.contentType == ContentTypeBlock && !keepBlockDev { deactivated, err = d.deactivateVolume(volDevPath) if err != nil { return false, err From 8ce875a18270d485c04c834009adb137094c02ab Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:22:16 +0100 Subject: [PATCH 14/18] lxd/storage/drivers/driver/lvm/volumes: UnmountTask usage Signed-off-by: Thomas Parrott --- lxd/storage/drivers/driver_lvm_volumes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage/drivers/driver_lvm_volumes.go b/lxd/storage/drivers/driver_lvm_volumes.go index 614e3b215b..518d2686a9 100644 --- a/lxd/storage/drivers/driver_lvm_volumes.go +++ b/lxd/storage/drivers/driver_lvm_volumes.go @@ -580,7 +580,7 @@ func (d *lvm) RenameVolume(vol Volume, newVolName string, op *operations.Operati revert.Success() return nil - }, op) + }, false, op) } // MigrateVolume sends a volume for migration. From a351f11164005d251914628d1439ec60ab08a8fe Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:22:48 +0100 Subject: [PATCH 15/18] lxd/storage/drivers/driver/ceph/volumes: d.UnmountVolume usage Signed-off-by: Thomas Parrott --- lxd/storage/drivers/driver_ceph_volumes.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lxd/storage/drivers/driver_ceph_volumes.go b/lxd/storage/drivers/driver_ceph_volumes.go index 55ef3825d6..0d3508eb94 100644 --- a/lxd/storage/drivers/driver_ceph_volumes.go +++ b/lxd/storage/drivers/driver_ceph_volumes.go @@ -610,7 +610,7 @@ func (d *ceph) DeleteVolume(vol Volume, op *operations.Operation) error { if vol.volType == VolumeTypeImage { // Try to umount but don't fail. - d.UnmountVolume(vol, op) + d.UnmountVolume(vol, false, op) // Check if image has dependant snapshots. _, err := d.rbdListSnapshotClones(vol, "readonly") @@ -658,7 +658,7 @@ func (d *ceph) DeleteVolume(vol Volume, op *operations.Operation) error { return err } } else { - _, err := d.UnmountVolume(vol, op) + _, err := d.UnmountVolume(vol, false, op) if err != nil { return err } @@ -980,7 +980,7 @@ func (d *ceph) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) // For VMs, unmount the filesystem volume. if vol.IsVMBlock() { fsVol := vol.NewVMBlockFilesystemVolume() - return d.UnmountVolume(fsVol, op) + return d.UnmountVolume(fsVol, false, op) } return false, nil @@ -991,7 +991,7 @@ func (d *ceph) RenameVolume(vol Volume, newName string, op *operations.Operation revert := revert.New() defer revert.Fail() - _, err := d.UnmountVolume(vol, op) + _, err := d.UnmountVolume(vol, false, op) if err != nil { return err } @@ -1419,7 +1419,7 @@ func (d *ceph) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, // RestoreVolume restores a volume from a snapshot. func (d *ceph) RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error { - ourUmount, err := d.UnmountVolume(vol, op) + ourUmount, err := d.UnmountVolume(vol, false, op) if err != nil { return err } From d6d0f9bf1faa4776b561c36779f3289d43c028b1 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:23:11 +0100 Subject: [PATCH 16/18] lxd/storage/drivers/driver/ceph/volumes: Adds keepBlockDev arg to UnmountVolume Signed-off-by: Thomas Parrott --- lxd/storage/drivers/driver_ceph_volumes.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lxd/storage/drivers/driver_ceph_volumes.go b/lxd/storage/drivers/driver_ceph_volumes.go index 0d3508eb94..808d1ea5c6 100644 --- a/lxd/storage/drivers/driver_ceph_volumes.go +++ b/lxd/storage/drivers/driver_ceph_volumes.go @@ -950,7 +950,8 @@ func (d *ceph) MountVolume(vol Volume, op *operations.Operation) (bool, error) { } // UnmountVolume simulates unmounting a volume. -func (d *ceph) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) { +// keepBlockDev indicates if backing block device should be not be unmapped if volume is unmounted. +func (d *ceph) UnmountVolume(vol Volume, keepBlockDev bool, op *operations.Operation) (bool, error) { // Attempt to unmount the volume. mountPath := vol.MountPath() if vol.contentType == ContentTypeFS && shared.IsMountPoint(mountPath) { @@ -958,18 +959,20 @@ func (d *ceph) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) if err != nil { return false, err } - d.logger.Debug("Unmounted RBD volume", log.Ctx{"path": mountPath}) + d.logger.Debug("Unmounted RBD volume", log.Ctx{"path": mountPath, "keepBlockDev": keepBlockDev}) // Attempt to unmap. - err = d.rbdUnmapVolume(vol, true) - if err != nil { - return false, err + if !keepBlockDev { + err = d.rbdUnmapVolume(vol, true) + if err != nil { + return false, err + } } return true, nil } - if vol.contentType == ContentTypeBlock { + if vol.contentType == ContentTypeBlock && !keepBlockDev { // Attempt to unmap. err := d.rbdUnmapVolume(vol, true) if err != nil { From 7ef06a6ca186224f8b8b80ec27f1947c40cccf67 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:23:29 +0100 Subject: [PATCH 17/18] lxd/storage/drivers/driver/zfs/volumes: Adds keepBlockDev arg to UnmountVolume Signed-off-by: Thomas Parrott --- lxd/storage/drivers/driver_zfs_volumes.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go index 0f06215306..9e1eeba331 100644 --- a/lxd/storage/drivers/driver_zfs_volumes.go +++ b/lxd/storage/drivers/driver_zfs_volumes.go @@ -1073,21 +1073,22 @@ func (d *zfs) MountVolume(vol Volume, op *operations.Operation) (bool, error) { } // UnmountVolume simulates unmounting a volume. -func (d *zfs) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) { +// keepBlockDev indicates if backing block device should be not be deactivated if volume is unmounted. +func (d *zfs) UnmountVolume(vol Volume, keepBlockDev bool, op *operations.Operation) (bool, error) { mountPath := vol.MountPath() dataset := d.dataset(vol, false) // For VMs, also mount the filesystem dataset. if vol.IsVMBlock() { fsVol := vol.NewVMBlockFilesystemVolume() - _, err := d.UnmountVolume(fsVol, op) + _, err := d.UnmountVolume(fsVol, false, op) if err != nil { return false, err } } // For block devices, we make them disappear. - if vol.contentType == ContentTypeBlock { + if vol.contentType == ContentTypeBlock && !keepBlockDev { err := d.setDatasetProperties(dataset, "volmode=none") if err != nil { return false, err From f74fd527955beb54259d7f5657af687fc4dc29c4 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 21 Oct 2020 11:23:42 +0100 Subject: [PATCH 18/18] lxd/storage/drivers/driver/zfs/volumes: d.UnmountVolume usage Signed-off-by: Thomas Parrott --- lxd/storage/drivers/driver_zfs_volumes.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go index 9e1eeba331..7689718384 100644 --- a/lxd/storage/drivers/driver_zfs_volumes.go +++ b/lxd/storage/drivers/driver_zfs_volumes.go @@ -416,7 +416,7 @@ func (d *zfs) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData } postHook = func(vol Volume) error { - _, err := d.UnmountVolume(vol, op) + _, err := d.UnmountVolume(vol, false, op) return err } } @@ -1184,7 +1184,7 @@ func (d *zfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *mig return err } if ourMount { - defer d.UnmountVolume(parentVol, op) + defer d.UnmountVolume(parentVol, false, op) } return genericVFSMigrateVolume(d, d.state, vol, conn, volSrcArgs, op) @@ -1288,7 +1288,7 @@ func (d *zfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWrit } if ourMount { - defer d.UnmountVolume(parentVol, op) + defer d.UnmountVolume(parentVol, false, op) } } From lxc-bot at linuxcontainers.org Wed Oct 21 16:37:12 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Wed, 21 Oct 2020 09:37:12 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/storage/zfs: Fix argument ordering Message-ID: <5f9063b8.1c69fb81.4c291.90b3SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 453 bytes Desc: not available URL: -------------- next part -------------- From da09d9fec580938778af2daaa199e88cec0303ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 21 Oct 2020 12:36:30 -0400 Subject: [PATCH] lxd/storage/zfs: Fix argument ordering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some distributions, the argument parser is very picky and needs options to be passed first. Signed-off-by: Stéphane Graber --- lxd/storage/drivers/driver_zfs_volumes.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go index 0f06215306..dd5de84e3a 100644 --- a/lxd/storage/drivers/driver_zfs_volumes.go +++ b/lxd/storage/drivers/driver_zfs_volumes.go @@ -565,17 +565,15 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool } } else { // Perform volume clone. - args := []string{ - "clone", - srcSnapshot, - d.dataset(vol, false), - } + args := []string{"clone"} if vol.contentType == ContentTypeBlock { // Use volmode=none so volume is invisible until mounted. args = append(args, "-o", "volmode=none") } + args = append(args, srcSnapshot, d.dataset(vol, false)) + // Clone the snapshot. _, err := shared.RunCommand("zfs", args...) if err != nil { From noreply at github.com Wed Oct 21 20:52:23 2020 From: noreply at github.com (Christian Brauner) Date: Wed, 21 Oct 2020 13:52:23 -0700 Subject: [lxc-devel] [lxc/lxc] 0c5d9c: conf: always send response to parent waiting for d... Message-ID: Branch: refs/heads/stable-4.0 Home: https://github.com/lxc/lxc Commit: 0c5d9c4579a44ae73c637b04f1f5b716833b78a3 https://github.com/lxc/lxc/commit/0c5d9c4579a44ae73c637b04f1f5b716833b78a3 Author: Christian Brauner Date: 2020-10-21 (Wed, 21 Oct 2020) Changed paths: M src/lxc/conf.c M src/lxc/conf.h M src/lxc/start.c Log Message: ----------- conf: always send response to parent waiting for devptfs_fd When no devpts devices are requested we used to return early but did not send a response to the parent. This is a problem because the parent will be waiting for a devpts fd to be sent. Make sure to always send a response. Signed-off-by: Christian Brauner Commit: fd40394adcc2e0ca9e9ebd2a10f3acc0122ddaa4 https://github.com/lxc/lxc/commit/fd40394adcc2e0ca9e9ebd2a10f3acc0122ddaa4 Author: Christian Brauner Date: 2020-10-21 (Wed, 21 Oct 2020) Changed paths: M src/lxc/conf.c Log Message: ----------- conf: account for early return when sending devpts fd Signed-off-by: Christian Brauner Compare: https://github.com/lxc/lxc/compare/90a3b1da8177...fd40394adcc2 From noreply at github.com Wed Oct 21 23:13:45 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:13:45 -0700 Subject: [lxc-devel] [lxc/lxc] 7bae22: Release LXC 4.0.5 Message-ID: Branch: refs/heads/stable-4.0 Home: https://github.com/lxc/lxc Commit: 7bae22f73db9e3ce99534ca0b60137a8a1bd973f https://github.com/lxc/lxc/commit/7bae22f73db9e3ce99534ca0b60137a8a1bd973f Author: Stéphane Graber Date: 2020-10-21 (Wed, 21 Oct 2020) Changed paths: M configure.ac Log Message: ----------- Release LXC 4.0.5 Signed-off-by: Stéphane Graber From noreply at github.com Wed Oct 21 23:13:49 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:13:49 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/mount-injection Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:13:50 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:13:50 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-0.7.4 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:13:51 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:13:51 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/stgraber/master Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:13:52 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:13:52 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-1.1 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:13:53 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:13:53 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/master Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:13:53 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:13:53 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-2.0 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:13:55 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:13:55 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-3.0 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:13:55 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:13:55 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-4.0 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:13:56 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:13:56 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-2.1 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:13:57 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:13:57 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-1.0 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:13:58 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:13:58 -0700 Subject: [lxc-devel] [lxc/lxc] 8d8512: voidlinux: Use an https mirror Message-ID: Branch: refs/heads/template-security Home: https://github.com/lxc/lxc Commit: 8d8512adcfacf23729ad172d83ad223e998fcb8f https://github.com/lxc/lxc/commit/8d8512adcfacf23729ad172d83ad223e998fcb8f Author: Stéphane Graber Date: 2017-02-03 (Fri, 03 Feb 2017) Changed paths: M templates/lxc-voidlinux.in Log Message: ----------- voidlinux: Use an https mirror Signed-off-by: Stéphane Graber Commit: 3c28574f478d041782d144aed12ee0a422ac0ea5 https://github.com/lxc/lxc/commit/3c28574f478d041782d144aed12ee0a422ac0ea5 Author: Stéphane Graber Date: 2017-02-08 (Wed, 08 Feb 2017) Changed paths: M templates/lxc-cirros.in Log Message: ----------- cirros: Use https for image download Signed-off-by: Stéphane Graber Compare: https://github.com/lxc/lxc/compare/8d8512adcfac%5E...3c28574f478d From noreply at github.com Wed Oct 21 23:14:46 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:14:46 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/template-security Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:14:55 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:14:55 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/stgraber/master Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:15:02 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:15:02 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/mount-injection Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:15:08 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:15:08 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-4.0 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:15:17 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:15:17 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-3.0 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:15:25 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:15:25 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-2.1 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:15:32 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:15:32 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-2.0 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:15:37 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:15:37 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-1.1 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:15:41 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:15:41 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-1.0 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:15:47 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:15:47 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/stable-0.7.4 Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:15:53 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:15:53 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/heads/lxc/master Home: https://github.com/lxc/lxc From noreply at github.com Wed Oct 21 23:16:37 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 21 Oct 2020 16:16:37 -0700 Subject: [lxc-devel] [lxc/lxc] Message-ID: Branch: refs/tags/lxc-4.0.5 Home: https://github.com/lxc/lxc From builds at travis-ci.org Wed Oct 21 23:25:53 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 21 Oct 2020 23:25:53 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7905 (mount-injection - ecce75a) In-Reply-To: Message-ID: <5f90c3813a2ae_13ff6df4d45e8217818@travis-tasks-7f9fb86864-748hm.mail> Build Update for lxc/lxc ------------------------------------- Build: #7905 Status: Errored Duration: 1 min and 28 secs Commit: ecce75a (mount-injection) Author: Christian Brauner Message: lxccontainer: fix temporary path removal Needed-by: https://github.com/lxc/lxd/issues/5227 Signed-off-by: Christian Brauner View the changeset: https://github.com/lxc/lxc/compare/mount-injection View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737867929?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From builds at travis-ci.org Wed Oct 21 23:27:43 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 21 Oct 2020 23:27:43 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7906 (stgraber/master - e14546e) In-Reply-To: Message-ID: <5f90c3ef856dc_13ff6e0b1cb482196ce@travis-tasks-7f9fb86864-748hm.mail> Build Update for lxc/lxc ------------------------------------- Build: #7906 Status: Errored Duration: 34 secs Commit: e14546e (stgraber/master) Author: Stéphane Graber Message: lxc-download: Fix retry loop Closes #3511 Signed-off-by: Stéphane Graber View the changeset: https://github.com/lxc/lxc/compare/stgraber/master View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737867976?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From builds at travis-ci.org Wed Oct 21 23:28:28 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 21 Oct 2020 23:28:28 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7907 (lxc/stable-1.1 - ce4f39c) In-Reply-To: Message-ID: <5f90c41c72f92_13f99c26483181931cd@travis-tasks-7f9fb86864-t6k2f.mail> Build Update for lxc/lxc ------------------------------------- Build: #7907 Status: Errored Duration: 42 secs Commit: ce4f39c (lxc/stable-1.1) Author: Stéphane Graber Message: ubuntu-cloud: Various fixes - Update list of supported releases - Make the fallback release trusty - Don't specify the compression algorithm (use auto-detection) so that people passing tarballs to the template don't see regressions. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn View the changeset: https://github.com/lxc/lxc/compare/lxc/stable-1.1 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737867985?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From builds at travis-ci.org Wed Oct 21 23:29:47 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 21 Oct 2020 23:29:47 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7908 (lxc/master - c639f45) In-Reply-To: Message-ID: <5f90c46bac60_13ff6df4d43182226aa@travis-tasks-7f9fb86864-748hm.mail> Build Update for lxc/lxc ------------------------------------- Build: #7908 Status: Errored Duration: 1 min and 18 secs Commit: c639f45 (lxc/master) Author: Stéphane Graber Message: Merge pull request #3559 from brauner/2020-10-20/fixes conf: account for early return when sending devpts fd View the changeset: https://github.com/lxc/lxc/compare/lxc/master View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737867988?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From builds at travis-ci.org Wed Oct 21 23:30:35 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 21 Oct 2020 23:30:35 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7909 (lxc/stable-2.0 - 8c23867) In-Reply-To: Message-ID: <5f90c49b3f71_13f99c8b22e141971bc@travis-tasks-7f9fb86864-t6k2f.mail> Build Update for lxc/lxc ------------------------------------- Build: #7909 Status: Errored Duration: 34 secs Commit: 8c23867 (lxc/stable-2.0) Author: Stéphane Graber Message: change version to 2.0.11 in configure.ac Signed-off-by: Stéphane Graber View the changeset: https://github.com/lxc/lxc/compare/lxc/stable-2.0 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737867997?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From builds at travis-ci.org Wed Oct 21 23:30:40 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 21 Oct 2020 23:30:40 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7910 (lxc/stable-3.0 - 813e65a) In-Reply-To: Message-ID: <5f90c4a01dd7e_13f99c7caa10c1973f4@travis-tasks-7f9fb86864-t6k2f.mail> Build Update for lxc/lxc ------------------------------------- Build: #7910 Status: Errored Duration: 37 secs Commit: 813e65a (lxc/stable-3.0) Author: Stéphane Graber Message: lxc.pc: Fix invalid @DLOG_LIBS@ Signed-off-by: Stéphane Graber View the changeset: https://github.com/lxc/lxc/compare/lxc/stable-3.0 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737868000?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From builds at travis-ci.org Wed Oct 21 23:32:35 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 21 Oct 2020 23:32:35 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7911 (lxc/stable-4.0 - 7bae22f) In-Reply-To: Message-ID: <5f90c512e1df3_13f99c7caa8dc19952@travis-tasks-7f9fb86864-t6k2f.mail> Build Update for lxc/lxc ------------------------------------- Build: #7911 Status: Errored Duration: 15 secs Commit: 7bae22f (lxc/stable-4.0) Author: Stéphane Graber Message: Release LXC 4.0.5 Signed-off-by: Stéphane Graber View the changeset: https://github.com/lxc/lxc/compare/lxc/stable-4.0 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737868003?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From builds at travis-ci.org Wed Oct 21 23:33:10 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 21 Oct 2020 23:33:10 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7912 (lxc/stable-2.1 - 76e5185) In-Reply-To: Message-ID: <5f90c535844e8_13ff6d6b21d782262a4@travis-tasks-7f9fb86864-748hm.mail> Build Update for lxc/lxc ------------------------------------- Build: #7912 Status: Errored Duration: 40 secs Commit: 76e5185 (lxc/stable-2.1) Author: Christian Brauner Message: tests: remove invalid parts of short lived tests Signed-off-by: Christian Brauner View the changeset: https://github.com/lxc/lxc/compare/lxc/stable-2.1 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737868012?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From builds at travis-ci.org Wed Oct 21 23:33:29 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 21 Oct 2020 23:33:29 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7913 (lxc/stable-1.0 - 74941f9) In-Reply-To: Message-ID: <5f90c548cd753_13f99c7cab05c2017fb@travis-tasks-7f9fb86864-t6k2f.mail> Build Update for lxc/lxc ------------------------------------- Build: #7913 Status: Errored Duration: 47 secs Commit: 74941f9 (lxc/stable-1.0) Author: Li Feng Message: start: dup std{in,out,err} to pty slave In the case the container has a console with a valid slave pty file descriptor we duplicate std{in,out,err} to the slave file descriptor so console logging works correctly. Also, we should become session leader. Closes #1646. Closes #1951. Signed-off-by: Li Feng Signed-off-by: Christian Brauner View the changeset: https://github.com/lxc/lxc/compare/lxc/stable-1.0 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737868015?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From builds at travis-ci.org Wed Oct 21 23:34:02 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 21 Oct 2020 23:34:02 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7914 (template-security - 3c28574) In-Reply-To: Message-ID: <5f90c56a39cc1_13ff6df4d4b8822662d@travis-tasks-7f9fb86864-748hm.mail> Build Update for lxc/lxc ------------------------------------- Build: #7914 Status: Errored Duration: 35 secs Commit: 3c28574 (template-security) Author: Stéphane Graber Message: cirros: Use https for image download Signed-off-by: Stéphane Graber View the changeset: https://github.com/lxc/lxc/compare/8d8512adcfac^...3c28574f478d View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737868018?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From builds at travis-ci.org Wed Oct 21 23:44:19 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 21 Oct 2020 23:44:19 +0000 Subject: [lxc-devel] Passed: lxc/lxc#7915 (lxc-4.0.5 - 7bae22f) In-Reply-To: Message-ID: <5f90c7d3629bb_13ff6e0b1cb4823524f@travis-tasks-7f9fb86864-748hm.mail> Build Update for lxc/lxc ------------------------------------- Build: #7915 Status: Passed Duration: 10 mins and 7 secs Commit: 7bae22f (lxc-4.0.5) Author: Stéphane Graber Message: Release LXC 4.0.5 Signed-off-by: Stéphane Graber View the changeset: https://github.com/lxc/lxc/compare/lxc-4.0.5 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/737868398?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lxc-bot at linuxcontainers.org Thu Oct 22 08:17:44 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Thu, 22 Oct 2020 01:17:44 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Storage: Fixes forwarded response if volume is remote to support projects Message-ID: <5f914028.1c69fb81.1097c.f607SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 383 bytes Desc: not available URL: -------------- next part -------------- From d520357207a0bb535f78212b25372aaf6478c9c5 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 09:15:04 +0100 Subject: [PATCH 1/4] lxd/cluster/connect: Renames project arg to projectName in ConnectIfInstanceIsRemote Signed-off-by: Thomas Parrott --- lxd/cluster/connect.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/cluster/connect.go b/lxd/cluster/connect.go index 7ed1dd0773..a180d796f8 100644 --- a/lxd/cluster/connect.go +++ b/lxd/cluster/connect.go @@ -97,11 +97,11 @@ func Connect(address string, cert *shared.CertInfo, notify bool) (lxd.InstanceSe // running the container with the given name. If it's not the local node will // connect to it and return the connected client, otherwise it will just return // nil. -func ConnectIfInstanceIsRemote(cluster *db.Cluster, project, name string, cert *shared.CertInfo, instanceType instancetype.Type) (lxd.InstanceServer, error) { +func ConnectIfInstanceIsRemote(cluster *db.Cluster, projectName string, name string, cert *shared.CertInfo, instanceType instancetype.Type) (lxd.InstanceServer, error) { var address string // Node address err := cluster.Transaction(func(tx *db.ClusterTx) error { var err error - address, err = tx.GetNodeAddressOfInstance(project, name, instanceType) + address, err = tx.GetNodeAddressOfInstance(projectName, name, instanceType) return err }) if err != nil { From a5a1f424b9cdba09d91fd86532cdde51d896b253 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 09:15:21 +0100 Subject: [PATCH 2/4] lxd/cluster/connect: Adds projectName arg to ConnectIfVolumeIsRemote Rather than being hardcoded to "default" project. Signed-off-by: Thomas Parrott --- lxd/cluster/connect.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/cluster/connect.go b/lxd/cluster/connect.go index a180d796f8..1d93cef6bf 100644 --- a/lxd/cluster/connect.go +++ b/lxd/cluster/connect.go @@ -121,11 +121,11 @@ func ConnectIfInstanceIsRemote(cluster *db.Cluster, projectName string, name str // // If there is more than one node with a matching volume name, an error is // returned. -func ConnectIfVolumeIsRemote(cluster *db.Cluster, poolID int64, volumeName string, volumeType int, cert *shared.CertInfo) (lxd.InstanceServer, error) { +func ConnectIfVolumeIsRemote(cluster *db.Cluster, poolID int64, projectName string, volumeName string, volumeType int, cert *shared.CertInfo) (lxd.InstanceServer, error) { var addresses []string // Node addresses err := cluster.Transaction(func(tx *db.ClusterTx) error { var err error - addresses, err = tx.GetStorageVolumeNodeAddresses(poolID, "default", volumeName, volumeType) + addresses, err = tx.GetStorageVolumeNodeAddresses(poolID, projectName, volumeName, volumeType) return err }) if err != nil { From 845d371084edd548435fb26bc8cfa293b8b06705 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 09:15:54 +0100 Subject: [PATCH 3/4] lxd/response: Adds projectName argument to forwardedResponseIfVolumeIsRemote Signed-off-by: Thomas Parrott --- lxd/response.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/response.go b/lxd/response.go index 01c3c9c605..77557643cb 100644 --- a/lxd/response.go +++ b/lxd/response.go @@ -59,13 +59,13 @@ func forwardedResponseIfInstanceIsRemote(d *Daemon, r *http.Request, project, na // // This is used when no targetNode is specified, and saves users some typing // when the volume name/type is unique to a node. -func forwardedResponseIfVolumeIsRemote(d *Daemon, r *http.Request, poolID int64, volumeName string, volumeType int) response.Response { +func forwardedResponseIfVolumeIsRemote(d *Daemon, r *http.Request, poolID int64, projectName string, volumeName string, volumeType int) response.Response { if queryParam(r, "target") != "" { return nil } cert := d.endpoints.NetworkCert() - client, err := cluster.ConnectIfVolumeIsRemote(d.cluster, poolID, volumeName, volumeType, cert) + client, err := cluster.ConnectIfVolumeIsRemote(d.cluster, poolID, projectName, volumeName, volumeType, cert) if err != nil && err != db.ErrNoSuchObject { return response.SmartError(err) } From ca3badc5dc32930ab754615b4eae6763b2f6149b Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 09:16:19 +0100 Subject: [PATCH 4/4] lxd/storage/volumes: forwardedResponseIfVolumeIsRemote projectName argument usage Signed-off-by: Thomas Parrott --- lxd/storage_volumes.go | 10 +++++----- lxd/storage_volumes_backup.go | 12 ++++++------ lxd/storage_volumes_snapshot.go | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lxd/storage_volumes.go b/lxd/storage_volumes.go index a7c0f6c4ca..e823b181cd 100644 --- a/lxd/storage_volumes.go +++ b/lxd/storage_volumes.go @@ -634,7 +634,7 @@ func storagePoolVolumeTypePost(d *Daemon, r *http.Request, volumeTypeName string return response.BadRequest(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, volumeType) if resp != nil { return resp } @@ -857,7 +857,7 @@ func storagePoolVolumeTypeGet(d *Daemon, r *http.Request, volumeTypeName string) return resp } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, volumeType) if resp != nil { return resp } @@ -936,7 +936,7 @@ func storagePoolVolumeTypePut(d *Daemon, r *http.Request, volumeTypeName string) return resp } - resp = forwardedResponseIfVolumeIsRemote(d, r, pool.ID(), volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, pool.ID(), projectName, volumeName, volumeType) if resp != nil { return resp } @@ -1078,7 +1078,7 @@ func storagePoolVolumeTypePatch(d *Daemon, r *http.Request, volumeTypeName strin return resp } - resp = forwardedResponseIfVolumeIsRemote(d, r, pool.ID(), volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, pool.ID(), projectName, volumeName, volumeType) if resp != nil { return resp } @@ -1176,7 +1176,7 @@ func storagePoolVolumeTypeDelete(d *Daemon, r *http.Request, volumeTypeName stri return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, volumeType) if resp != nil { return resp } diff --git a/lxd/storage_volumes_backup.go b/lxd/storage_volumes_backup.go index 2979481bbe..2b85e6f6b8 100644 --- a/lxd/storage_volumes_backup.go +++ b/lxd/storage_volumes_backup.go @@ -72,7 +72,7 @@ func storagePoolVolumeTypeCustomBackupsGet(d *Daemon, r *http.Request) response. } // Handle requests targeted to a volume on a different node - resp := forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, db.StoragePoolVolumeTypeCustom) + resp := forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -145,7 +145,7 @@ func storagePoolVolumeTypeCustomBackupsPost(d *Daemon, r *http.Request) response return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -287,7 +287,7 @@ func storagePoolVolumeTypeCustomBackupGet(d *Daemon, r *http.Request) response.R return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -338,7 +338,7 @@ func storagePoolVolumeTypeCustomBackupPost(d *Daemon, r *http.Request) response. return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -420,7 +420,7 @@ func storagePoolVolumeTypeCustomBackupDelete(d *Daemon, r *http.Request) respons return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -489,7 +489,7 @@ func storagePoolVolumeTypeCustomBackupExportGet(d *Daemon, r *http.Request) resp return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } diff --git a/lxd/storage_volumes_snapshot.go b/lxd/storage_volumes_snapshot.go index 9878c0d822..d766a8f7db 100644 --- a/lxd/storage_volumes_snapshot.go +++ b/lxd/storage_volumes_snapshot.go @@ -114,7 +114,7 @@ func storagePoolVolumeSnapshotsTypePost(d *Daemon, r *http.Request) response.Res return resp } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, volumeType) if resp != nil { return resp } @@ -302,7 +302,7 @@ func storagePoolVolumeSnapshotTypePost(d *Daemon, r *http.Request) response.Resp } fullSnapshotName := fmt.Sprintf("%s/%s", volumeName, snapshotName) - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, fullSnapshotName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, fullSnapshotName, volumeType) if resp != nil { return resp } @@ -368,7 +368,7 @@ func storagePoolVolumeSnapshotTypeGet(d *Daemon, r *http.Request) response.Respo } fullSnapshotName := fmt.Sprintf("%s/%s", volumeName, snapshotName) - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, fullSnapshotName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, fullSnapshotName, volumeType) if resp != nil { return resp } @@ -437,7 +437,7 @@ func storagePoolVolumeSnapshotTypePut(d *Daemon, r *http.Request) response.Respo } fullSnapshotName := fmt.Sprintf("%s/%s", volumeName, snapshotName) - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, fullSnapshotName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, fullSnapshotName, volumeType) if resp != nil { return resp } @@ -533,7 +533,7 @@ func storagePoolVolumeSnapshotTypeDelete(d *Daemon, r *http.Request) response.Re } fullSnapshotName := fmt.Sprintf("%s/%s", volumeName, snapshotName) - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, fullSnapshotName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, fullSnapshotName, volumeType) if resp != nil { return resp } From lxc-bot at linuxcontainers.org Thu Oct 22 11:45:07 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Thu, 22 Oct 2020 04:45:07 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Tp storage forwarded response if volume is remote exclusive volumes Message-ID: <5f9170c3.1c69fb81.feff3.2fe4SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From d520357207a0bb535f78212b25372aaf6478c9c5 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 09:15:04 +0100 Subject: [PATCH 01/14] lxd/cluster/connect: Renames project arg to projectName in ConnectIfInstanceIsRemote Signed-off-by: Thomas Parrott --- lxd/cluster/connect.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/cluster/connect.go b/lxd/cluster/connect.go index 7ed1dd0773..a180d796f8 100644 --- a/lxd/cluster/connect.go +++ b/lxd/cluster/connect.go @@ -97,11 +97,11 @@ func Connect(address string, cert *shared.CertInfo, notify bool) (lxd.InstanceSe // running the container with the given name. If it's not the local node will // connect to it and return the connected client, otherwise it will just return // nil. -func ConnectIfInstanceIsRemote(cluster *db.Cluster, project, name string, cert *shared.CertInfo, instanceType instancetype.Type) (lxd.InstanceServer, error) { +func ConnectIfInstanceIsRemote(cluster *db.Cluster, projectName string, name string, cert *shared.CertInfo, instanceType instancetype.Type) (lxd.InstanceServer, error) { var address string // Node address err := cluster.Transaction(func(tx *db.ClusterTx) error { var err error - address, err = tx.GetNodeAddressOfInstance(project, name, instanceType) + address, err = tx.GetNodeAddressOfInstance(projectName, name, instanceType) return err }) if err != nil { From a5a1f424b9cdba09d91fd86532cdde51d896b253 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 09:15:21 +0100 Subject: [PATCH 02/14] lxd/cluster/connect: Adds projectName arg to ConnectIfVolumeIsRemote Rather than being hardcoded to "default" project. Signed-off-by: Thomas Parrott --- lxd/cluster/connect.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/cluster/connect.go b/lxd/cluster/connect.go index a180d796f8..1d93cef6bf 100644 --- a/lxd/cluster/connect.go +++ b/lxd/cluster/connect.go @@ -121,11 +121,11 @@ func ConnectIfInstanceIsRemote(cluster *db.Cluster, projectName string, name str // // If there is more than one node with a matching volume name, an error is // returned. -func ConnectIfVolumeIsRemote(cluster *db.Cluster, poolID int64, volumeName string, volumeType int, cert *shared.CertInfo) (lxd.InstanceServer, error) { +func ConnectIfVolumeIsRemote(cluster *db.Cluster, poolID int64, projectName string, volumeName string, volumeType int, cert *shared.CertInfo) (lxd.InstanceServer, error) { var addresses []string // Node addresses err := cluster.Transaction(func(tx *db.ClusterTx) error { var err error - addresses, err = tx.GetStorageVolumeNodeAddresses(poolID, "default", volumeName, volumeType) + addresses, err = tx.GetStorageVolumeNodeAddresses(poolID, projectName, volumeName, volumeType) return err }) if err != nil { From 845d371084edd548435fb26bc8cfa293b8b06705 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 09:15:54 +0100 Subject: [PATCH 03/14] lxd/response: Adds projectName argument to forwardedResponseIfVolumeIsRemote Signed-off-by: Thomas Parrott --- lxd/response.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/response.go b/lxd/response.go index 01c3c9c605..77557643cb 100644 --- a/lxd/response.go +++ b/lxd/response.go @@ -59,13 +59,13 @@ func forwardedResponseIfInstanceIsRemote(d *Daemon, r *http.Request, project, na // // This is used when no targetNode is specified, and saves users some typing // when the volume name/type is unique to a node. -func forwardedResponseIfVolumeIsRemote(d *Daemon, r *http.Request, poolID int64, volumeName string, volumeType int) response.Response { +func forwardedResponseIfVolumeIsRemote(d *Daemon, r *http.Request, poolID int64, projectName string, volumeName string, volumeType int) response.Response { if queryParam(r, "target") != "" { return nil } cert := d.endpoints.NetworkCert() - client, err := cluster.ConnectIfVolumeIsRemote(d.cluster, poolID, volumeName, volumeType, cert) + client, err := cluster.ConnectIfVolumeIsRemote(d.cluster, poolID, projectName, volumeName, volumeType, cert) if err != nil && err != db.ErrNoSuchObject { return response.SmartError(err) } From ca3badc5dc32930ab754615b4eae6763b2f6149b Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 09:16:19 +0100 Subject: [PATCH 04/14] lxd/storage/volumes: forwardedResponseIfVolumeIsRemote projectName argument usage Signed-off-by: Thomas Parrott --- lxd/storage_volumes.go | 10 +++++----- lxd/storage_volumes_backup.go | 12 ++++++------ lxd/storage_volumes_snapshot.go | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lxd/storage_volumes.go b/lxd/storage_volumes.go index a7c0f6c4ca..e823b181cd 100644 --- a/lxd/storage_volumes.go +++ b/lxd/storage_volumes.go @@ -634,7 +634,7 @@ func storagePoolVolumeTypePost(d *Daemon, r *http.Request, volumeTypeName string return response.BadRequest(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, volumeType) if resp != nil { return resp } @@ -857,7 +857,7 @@ func storagePoolVolumeTypeGet(d *Daemon, r *http.Request, volumeTypeName string) return resp } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, volumeType) if resp != nil { return resp } @@ -936,7 +936,7 @@ func storagePoolVolumeTypePut(d *Daemon, r *http.Request, volumeTypeName string) return resp } - resp = forwardedResponseIfVolumeIsRemote(d, r, pool.ID(), volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, pool.ID(), projectName, volumeName, volumeType) if resp != nil { return resp } @@ -1078,7 +1078,7 @@ func storagePoolVolumeTypePatch(d *Daemon, r *http.Request, volumeTypeName strin return resp } - resp = forwardedResponseIfVolumeIsRemote(d, r, pool.ID(), volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, pool.ID(), projectName, volumeName, volumeType) if resp != nil { return resp } @@ -1176,7 +1176,7 @@ func storagePoolVolumeTypeDelete(d *Daemon, r *http.Request, volumeTypeName stri return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, volumeType) if resp != nil { return resp } diff --git a/lxd/storage_volumes_backup.go b/lxd/storage_volumes_backup.go index 2979481bbe..2b85e6f6b8 100644 --- a/lxd/storage_volumes_backup.go +++ b/lxd/storage_volumes_backup.go @@ -72,7 +72,7 @@ func storagePoolVolumeTypeCustomBackupsGet(d *Daemon, r *http.Request) response. } // Handle requests targeted to a volume on a different node - resp := forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, db.StoragePoolVolumeTypeCustom) + resp := forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -145,7 +145,7 @@ func storagePoolVolumeTypeCustomBackupsPost(d *Daemon, r *http.Request) response return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -287,7 +287,7 @@ func storagePoolVolumeTypeCustomBackupGet(d *Daemon, r *http.Request) response.R return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -338,7 +338,7 @@ func storagePoolVolumeTypeCustomBackupPost(d *Daemon, r *http.Request) response. return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -420,7 +420,7 @@ func storagePoolVolumeTypeCustomBackupDelete(d *Daemon, r *http.Request) respons return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -489,7 +489,7 @@ func storagePoolVolumeTypeCustomBackupExportGet(d *Daemon, r *http.Request) resp return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } diff --git a/lxd/storage_volumes_snapshot.go b/lxd/storage_volumes_snapshot.go index 9878c0d822..d766a8f7db 100644 --- a/lxd/storage_volumes_snapshot.go +++ b/lxd/storage_volumes_snapshot.go @@ -114,7 +114,7 @@ func storagePoolVolumeSnapshotsTypePost(d *Daemon, r *http.Request) response.Res return resp } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, volumeType) if resp != nil { return resp } @@ -302,7 +302,7 @@ func storagePoolVolumeSnapshotTypePost(d *Daemon, r *http.Request) response.Resp } fullSnapshotName := fmt.Sprintf("%s/%s", volumeName, snapshotName) - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, fullSnapshotName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, fullSnapshotName, volumeType) if resp != nil { return resp } @@ -368,7 +368,7 @@ func storagePoolVolumeSnapshotTypeGet(d *Daemon, r *http.Request) response.Respo } fullSnapshotName := fmt.Sprintf("%s/%s", volumeName, snapshotName) - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, fullSnapshotName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, fullSnapshotName, volumeType) if resp != nil { return resp } @@ -437,7 +437,7 @@ func storagePoolVolumeSnapshotTypePut(d *Daemon, r *http.Request) response.Respo } fullSnapshotName := fmt.Sprintf("%s/%s", volumeName, snapshotName) - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, fullSnapshotName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, fullSnapshotName, volumeType) if resp != nil { return resp } @@ -533,7 +533,7 @@ func storagePoolVolumeSnapshotTypeDelete(d *Daemon, r *http.Request) response.Re } fullSnapshotName := fmt.Sprintf("%s/%s", volumeName, snapshotName) - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, fullSnapshotName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, fullSnapshotName, volumeType) if resp != nil { return resp } From d1b92fe24513fef9698c2c25d8a1459d2b658e88 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 09:29:29 +0100 Subject: [PATCH 05/14] lxd/db/storage/volumes: Corrects mispelled argument name in GetStorageVolumeNodeAddresses Signed-off-by: Thomas Parrott --- lxd/db/storage_volumes.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go index d6680de8a3..b4ef307423 100644 --- a/lxd/db/storage_volumes.go +++ b/lxd/db/storage_volumes.go @@ -628,7 +628,7 @@ type StorageVolumeArgs struct { // The volume name can be either a regular name or a volume snapshot name. // // The empty string is used in place of the address of the current node. -func (c *ClusterTx) GetStorageVolumeNodeAddresses(poolID int64, project, name string, typ int) ([]string, error) { +func (c *ClusterTx) GetStorageVolumeNodeAddresses(poolID int64, project, name string, volumeType int) ([]string, error) { nodes := []struct { id int64 address string @@ -656,7 +656,7 @@ SELECT nodes.id, nodes.address return nil, err } defer stmt.Close() - err = query.SelectObjects(stmt, dest, poolID, project, name, typ) + err = query.SelectObjects(stmt, dest, poolID, project, name, volumeType) if err != nil { return nil, err } From ac8a07df911434a6482abe72c6a360c1fa188ff4 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 12:37:46 +0100 Subject: [PATCH 06/14] lxd/db/errors: Adds ErrNoNode var used to indicate no node has been found for a resource Signed-off-by: Thomas Parrott --- lxd/db/errors.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lxd/db/errors.go b/lxd/db/errors.go index c1f046a7dc..1208a36343 100644 --- a/lxd/db/errors.go +++ b/lxd/db/errors.go @@ -16,4 +16,7 @@ var ( // isn't found so we don't abuse sql.ErrNoRows any more than we // already do. ErrNoSuchObject = fmt.Errorf("No such object") + + // ErrNoNode is used to indicate no node has been found for a resource. + ErrNoNode = fmt.Errorf("No node found") ) From aa23b4f8eb30b3558e8b073c563dc69f3f773ca8 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 12:39:12 +0100 Subject: [PATCH 07/14] lxd/db/storage/volumes: Modifies GetStorageVolumeNodeAddresses to detect volumes that are not bound to a single node Returns special error value in that case. Signed-off-by: Thomas Parrott --- lxd/db/storage_volumes.go | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go index b4ef307423..40cf6d33d6 100644 --- a/lxd/db/storage_volumes.go +++ b/lxd/db/storage_volumes.go @@ -628,7 +628,7 @@ type StorageVolumeArgs struct { // The volume name can be either a regular name or a volume snapshot name. // // The empty string is used in place of the address of the current node. -func (c *ClusterTx) GetStorageVolumeNodeAddresses(poolID int64, project, name string, volumeType int) ([]string, error) { +func (c *ClusterTx) GetStorageVolumeNodeAddresses(poolID int64, projectName string, volumeName string, volumeType int) ([]string, error) { nodes := []struct { id int64 address string @@ -642,27 +642,32 @@ func (c *ClusterTx) GetStorageVolumeNodeAddresses(poolID int64, project, name st } sql := ` -SELECT nodes.id, nodes.address - FROM nodes - JOIN storage_volumes_all ON storage_volumes_all.node_id=nodes.id - JOIN projects ON projects.id = storage_volumes_all.project_id - WHERE storage_volumes_all.storage_pool_id=? - AND projects.name=? - AND storage_volumes_all.name=? - AND storage_volumes_all.type=? + SELECT coalesce(nodes.id,0) AS nodeID, coalesce(nodes.address,"") AS nodeAddress + FROM storage_volumes_all + JOIN projects ON projects.id = storage_volumes_all.project_id + LEFT JOIN nodes ON storage_volumes_all.node_id=nodes.id + WHERE storage_volumes_all.storage_pool_id=? + AND projects.name=? + AND storage_volumes_all.name=? + AND storage_volumes_all.type=? ` stmt, err := c.tx.Prepare(sql) if err != nil { return nil, err } defer stmt.Close() - err = query.SelectObjects(stmt, dest, poolID, project, name, volumeType) + err = query.SelectObjects(stmt, dest, poolID, projectName, volumeName, volumeType) if err != nil { return nil, err } addresses := []string{} for _, node := range nodes { + // Volume is defined without a node. + if node.id == 0 && node.address == "" { + return nil, ErrNoNode + } + address := node.address if node.id == c.nodeID { address = "" From 4cc9d74e880558f43f2bb882344cee2050955501 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 12:40:16 +0100 Subject: [PATCH 08/14] lxd/db/storage/volumes: Renames StorageVolumeIsAvailable to StorageVolumeAttachedExclusive And modifies return value of function to return the remote instance that has the volume attached exclusively. This will be used to redirect API requests for the volume to the instance's node. Signed-off-by: Thomas Parrott --- lxd/db/storage_volumes.go | 116 +++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 64 deletions(-) diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go index 40cf6d33d6..d30dc21fab 100644 --- a/lxd/db/storage_volumes.go +++ b/lxd/db/storage_volumes.go @@ -812,70 +812,6 @@ SELECT storage_volumes_snapshots.name FROM storage_volumes_snapshots return max } -// StorageVolumeIsAvailable checks that if a custom volume available for being attached. -// -// Always return true for non-Ceph volumes. -// -// For Ceph volumes, return true if the volume is either not attached to any -// other container, or attached to containers on this node. -func (c *Cluster) StorageVolumeIsAvailable(pool, volume string) (bool, error) { - isAvailable := false - - err := c.Transaction(func(tx *ClusterTx) error { - id, err := tx.GetStoragePoolID(pool) - if err != nil { - return errors.Wrapf(err, "Fetch storage pool ID for %q", pool) - } - - driver, err := tx.GetStoragePoolDriver(id) - if err != nil { - return errors.Wrapf(err, "Fetch storage pool driver for %q", pool) - } - - if driver != "ceph" { - isAvailable = true - return nil - } - - node, err := tx.GetLocalNodeName() - if err != nil { - return errors.Wrapf(err, "Fetch node name") - } - - containers, err := tx.instanceListExpanded() - if err != nil { - return errors.Wrapf(err, "Fetch instances") - } - - for _, container := range containers { - for _, device := range container.Devices { - if device["type"] != "disk" { - continue - } - if device["pool"] != pool { - continue - } - if device["source"] != volume { - continue - } - if container.Node != node { - // This ceph volume is already attached - // to a container on a different node. - return nil - } - } - } - isAvailable = true - - return nil - }) - if err != nil { - return false, err - } - - return isAvailable, nil -} - // Updates the description of a storage volume. func storageVolumeDescriptionUpdate(tx *sql.Tx, volumeID int64, description string, isSnapshot bool) error { var table string @@ -1057,3 +993,55 @@ WHERE storage_volumes.type = ? AND projects.name = ? return volumes, nil } + +// StorageVolumeAttachedExclusive checks whether a custom volume available for being attached. +// Returns the remote instance that has the volume exclusively attached. If not returns nil if available. +func (c *ClusterTx) StorageVolumeAttachedExclusive(poolName string, volumeName string) (*Instance, error) { + id, err := c.GetStoragePoolID(poolName) + if err != nil { + return nil, errors.Wrapf(err, "Fetch storage pool ID for %q", poolName) + } + + driver, err := c.GetStoragePoolDriver(id) + if err != nil { + return nil, errors.Wrapf(err, "Fetch storage pool driver for %q", poolName) + } + + // Always return nil for non-Ceph volumes. + if driver != "ceph" { + return nil, nil + } + + // For Ceph volumes, return nil if the volume is either not attached to any other instance, + // or attached to instance(s) on this node. + node, err := c.GetLocalNodeName() + if err != nil { + return nil, errors.Wrapf(err, "Fetch node name") + } + + instances, err := c.instanceListExpanded() + if err != nil { + return nil, errors.Wrapf(err, "Fetch instances") + } + + for _, inst := range instances { + for _, device := range inst.Devices { + if device["type"] != "disk" { + continue + } + if device["pool"] != poolName { + continue + } + if device["source"] != volumeName { + continue + } + + if inst.Node != node { + // Volume is exclusively attached to an instance on a different node. + return &inst, nil + } + } + } + + return nil, nil +} From 108b941dab25306f77adf04c43d28bc3884ede74 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 12:41:58 +0100 Subject: [PATCH 09/14] lxd/cluster/connect: Modifies ConnectIfVolumeIsRemote to take poolName rather than poolID This is so it can use StorageVolumeAttachedExclusive when a custom volume is not bound to a specific node. Signed-off-by: Thomas Parrott --- lxd/cluster/connect.go | 74 ++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/lxd/cluster/connect.go b/lxd/cluster/connect.go index 1d93cef6bf..38d1d994a7 100644 --- a/lxd/cluster/connect.go +++ b/lxd/cluster/connect.go @@ -121,40 +121,72 @@ func ConnectIfInstanceIsRemote(cluster *db.Cluster, projectName string, name str // // If there is more than one node with a matching volume name, an error is // returned. -func ConnectIfVolumeIsRemote(cluster *db.Cluster, poolID int64, projectName string, volumeName string, volumeType int, cert *shared.CertInfo) (lxd.InstanceServer, error) { - var addresses []string // Node addresses +func ConnectIfVolumeIsRemote(cluster *db.Cluster, poolName string, projectName string, volumeName string, volumeType int, cert *shared.CertInfo) (lxd.InstanceServer, error) { + var address string + err := cluster.Transaction(func(tx *db.ClusterTx) error { - var err error - addresses, err = tx.GetStorageVolumeNodeAddresses(poolID, projectName, volumeName, volumeType) - return err - }) - if err != nil { - return nil, err - } + poolID, err := tx.GetStoragePoolID(poolName) + if err != nil { + return err + } - if len(addresses) > 1 { - var driver string - err := cluster.Transaction(func(tx *db.ClusterTx) error { - var err error - driver, err = tx.GetStoragePoolDriver(poolID) + addresses, err := tx.GetStorageVolumeNodeAddresses(poolID, projectName, volumeName, volumeType) + if err != nil && err != db.ErrNoNode { return err - }) - if err != nil { - return nil, err } - if driver == "ceph" || driver == "cephfs" { - return nil, nil + // If volume uses a remote storage driver and so has no explicit node, then we need to check + // whether it is exclusively attached to remote instance, and if so then we need to forward the + // request to the node where it is currently used. This avoids using the volume locally when that + // may cause issues when in use on another node. + if err == db.ErrNoNode { + remoteInstance, err := tx.StorageVolumeAttachedExclusive(poolName, volumeName) + if err != nil { + return fmt.Errorf("Check if volume is available: %v", err) + } + + if remoteInstance != nil { + node, err := tx.GetNodeByName(remoteInstance.Node) + if err != nil { + return fmt.Errorf("Check node of remote instance: %v", err) + } + + addresses = []string{node.Address} // Replace address list with instance's node. + } else { + // Volume isn't exclusively attached to an instance and has no fixed node. + addresses = []string{""} // Use local node. + } + } + + addressCount := len(addresses) + if addressCount > 1 { + driver, err := tx.GetStoragePoolDriver(poolID) + if err != nil { + return err + } + + if driver == "ceph" || driver == "cephfs" { + return nil + } + + return fmt.Errorf("More than one node has a volume named %q", volumeName) + } else if addressCount < 1 { + return fmt.Errorf("Volume %q has empty node list", volumeName) } - return nil, fmt.Errorf("more than one node has a volume named %s", volumeName) + address = addresses[0] + return nil + }) + if err != nil { + return nil, err } - address := addresses[0] + // Use local node. if address == "" { return nil, nil } + // Connect to remote node. return Connect(address, cert, false) } From 542520e533a3e9f9d0c69d2eeaa828436e05db5d Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 12:43:03 +0100 Subject: [PATCH 10/14] lxd/device/disk: tx.StorageVolumeAttachedExclusive updated usage Signed-off-by: Thomas Parrott --- lxd/device/disk.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lxd/device/disk.go b/lxd/device/disk.go index 4f728007e7..c76183a088 100644 --- a/lxd/device/disk.go +++ b/lxd/device/disk.go @@ -174,12 +174,19 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error { // device (check for instancetype.Any), and we have least one expanded device (this is so we only // do this expensive check after devices have been expanded). if instConf.Type() != instancetype.Any && len(instConf.ExpandedDevices()) > 0 && d.config["source"] != "" && d.config["path"] != "/" { - isAvailable, err := d.state.Cluster.StorageVolumeIsAvailable(d.config["pool"], d.config["source"]) + err := d.state.Cluster.Transaction(func(tx *db.ClusterTx) error { + remoteInstance, err := tx.StorageVolumeAttachedExclusive(d.config["pool"], d.config["source"]) + if err != nil { + return errors.Wrapf(err, "Failed checking if volume is exclusively attached") + } + if remoteInstance != nil { + return fmt.Errorf("Storage volume %q is already attached to an instance on a different node", d.config["source"]) + } + + return nil + }) if err != nil { - return fmt.Errorf("Check if volume is available: %v", err) - } - if !isAvailable { - return fmt.Errorf("Storage volume %q is already attached to an instance on a different node", d.config["source"]) + return err } } From b9fd0b0df5c999a95d502d8b519b571dcf1c9666 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 12:43:26 +0100 Subject: [PATCH 11/14] lxd/response: Updates forwardedResponseIfVolumeIsRemote to accept poolName rather than poolID Signed-off-by: Thomas Parrott --- lxd/response.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lxd/response.go b/lxd/response.go index 77557643cb..e61b9a7461 100644 --- a/lxd/response.go +++ b/lxd/response.go @@ -4,7 +4,6 @@ import ( "net/http" "github.com/lxc/lxd/lxd/cluster" - "github.com/lxc/lxd/lxd/db" "github.com/lxc/lxd/lxd/instance/instancetype" "github.com/lxc/lxd/lxd/response" ) @@ -59,18 +58,20 @@ func forwardedResponseIfInstanceIsRemote(d *Daemon, r *http.Request, project, na // // This is used when no targetNode is specified, and saves users some typing // when the volume name/type is unique to a node. -func forwardedResponseIfVolumeIsRemote(d *Daemon, r *http.Request, poolID int64, projectName string, volumeName string, volumeType int) response.Response { +func forwardedResponseIfVolumeIsRemote(d *Daemon, r *http.Request, poolName string, projectName string, volumeName string, volumeType int) response.Response { if queryParam(r, "target") != "" { return nil } cert := d.endpoints.NetworkCert() - client, err := cluster.ConnectIfVolumeIsRemote(d.cluster, poolID, projectName, volumeName, volumeType, cert) - if err != nil && err != db.ErrNoSuchObject { + client, err := cluster.ConnectIfVolumeIsRemote(d.cluster, poolName, projectName, volumeName, volumeType, cert) + if err != nil { return response.SmartError(err) } + if client == nil { return nil } + return response.ForwardedResponse(client, r) } From 386410d99b768a12719db8582a2d8f36258f146d Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 12:43:56 +0100 Subject: [PATCH 12/14] lxd/storage/volumes: forwardedResponseIfVolumeIsRemote usage Signed-off-by: Thomas Parrott --- lxd/storage_volumes.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/lxd/storage_volumes.go b/lxd/storage_volumes.go index e823b181cd..28d4c8ef7d 100644 --- a/lxd/storage_volumes.go +++ b/lxd/storage_volumes.go @@ -634,7 +634,7 @@ func storagePoolVolumeTypePost(d *Daemon, r *http.Request, volumeTypeName string return response.BadRequest(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, volumeName, volumeType) if resp != nil { return resp } @@ -857,7 +857,7 @@ func storagePoolVolumeTypeGet(d *Daemon, r *http.Request, volumeTypeName string) return resp } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, volumeName, volumeType) if resp != nil { return resp } @@ -936,7 +936,7 @@ func storagePoolVolumeTypePut(d *Daemon, r *http.Request, volumeTypeName string) return resp } - resp = forwardedResponseIfVolumeIsRemote(d, r, pool.ID(), projectName, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, pool.Name(), projectName, volumeName, volumeType) if resp != nil { return resp } @@ -1078,7 +1078,7 @@ func storagePoolVolumeTypePatch(d *Daemon, r *http.Request, volumeTypeName strin return resp } - resp = forwardedResponseIfVolumeIsRemote(d, r, pool.ID(), projectName, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, pool.Name(), projectName, volumeName, volumeType) if resp != nil { return resp } @@ -1171,12 +1171,7 @@ func storagePoolVolumeTypeDelete(d *Daemon, r *http.Request, volumeTypeName stri return resp } - poolID, _, err := d.cluster.GetStoragePool(poolName) - if err != nil { - return response.SmartError(err) - } - - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, volumeName, volumeType) if resp != nil { return resp } From 5a51ea0e0ffe4aec7125210c7696e8572c70e6b2 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 12:44:16 +0100 Subject: [PATCH 13/14] lxd/storage/volumes/backup: forwardedResponseIfVolumeIsRemote usage Signed-off-by: Thomas Parrott --- lxd/storage_volumes_backup.go | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/lxd/storage_volumes_backup.go b/lxd/storage_volumes_backup.go index 2b85e6f6b8..b39d87ad8f 100644 --- a/lxd/storage_volumes_backup.go +++ b/lxd/storage_volumes_backup.go @@ -72,7 +72,7 @@ func storagePoolVolumeTypeCustomBackupsGet(d *Daemon, r *http.Request) response. } // Handle requests targeted to a volume on a different node - resp := forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) + resp := forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -145,7 +145,7 @@ func storagePoolVolumeTypeCustomBackupsPost(d *Daemon, r *http.Request) response return response.SmartError(err) } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -282,12 +282,7 @@ func storagePoolVolumeTypeCustomBackupGet(d *Daemon, r *http.Request) response.R return resp } - poolID, _, err := d.cluster.GetStoragePool(poolName) - if err != nil { - return response.SmartError(err) - } - - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -333,12 +328,7 @@ func storagePoolVolumeTypeCustomBackupPost(d *Daemon, r *http.Request) response. return resp } - poolID, _, err := d.cluster.GetStoragePool(poolName) - if err != nil { - return response.SmartError(err) - } - - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -415,12 +405,7 @@ func storagePoolVolumeTypeCustomBackupDelete(d *Daemon, r *http.Request) respons return resp } - poolID, _, err := d.cluster.GetStoragePool(poolName) - if err != nil { - return response.SmartError(err) - } - - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } @@ -484,12 +469,7 @@ func storagePoolVolumeTypeCustomBackupExportGet(d *Daemon, r *http.Request) resp return resp } - poolID, _, err := d.cluster.GetStoragePool(poolName) - if err != nil { - return response.SmartError(err) - } - - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, db.StoragePoolVolumeTypeCustom) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, volumeName, db.StoragePoolVolumeTypeCustom) if resp != nil { return resp } From 68f1906199abc68cc47f1f0f73d4c31434bd7ad3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 22 Oct 2020 12:44:38 +0100 Subject: [PATCH 14/14] lxd/storage/volumes/snapshot: forwardedResponseIfVolumeIsRemote Signed-off-by: Thomas Parrott --- lxd/storage_volumes_snapshot.go | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/lxd/storage_volumes_snapshot.go b/lxd/storage_volumes_snapshot.go index d766a8f7db..323f813659 100644 --- a/lxd/storage_volumes_snapshot.go +++ b/lxd/storage_volumes_snapshot.go @@ -114,7 +114,7 @@ func storagePoolVolumeSnapshotsTypePost(d *Daemon, r *http.Request) response.Res return resp } - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, volumeName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, volumeName, volumeType) if resp != nil { return resp } @@ -296,13 +296,8 @@ func storagePoolVolumeSnapshotTypePost(d *Daemon, r *http.Request) response.Resp return resp } - poolID, _, err := d.cluster.GetStoragePool(poolName) - if err != nil { - return response.SmartError(err) - } - fullSnapshotName := fmt.Sprintf("%s/%s", volumeName, snapshotName) - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, fullSnapshotName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, fullSnapshotName, volumeType) if resp != nil { return resp } @@ -368,7 +363,7 @@ func storagePoolVolumeSnapshotTypeGet(d *Daemon, r *http.Request) response.Respo } fullSnapshotName := fmt.Sprintf("%s/%s", volumeName, snapshotName) - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, fullSnapshotName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, fullSnapshotName, volumeType) if resp != nil { return resp } @@ -437,7 +432,7 @@ func storagePoolVolumeSnapshotTypePut(d *Daemon, r *http.Request) response.Respo } fullSnapshotName := fmt.Sprintf("%s/%s", volumeName, snapshotName) - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, fullSnapshotName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, fullSnapshotName, volumeType) if resp != nil { return resp } @@ -527,13 +522,8 @@ func storagePoolVolumeSnapshotTypeDelete(d *Daemon, r *http.Request) response.Re return resp } - poolID, _, err := d.cluster.GetStoragePool(poolName) - if err != nil { - return response.SmartError(err) - } - fullSnapshotName := fmt.Sprintf("%s/%s", volumeName, snapshotName) - resp = forwardedResponseIfVolumeIsRemote(d, r, poolID, projectName, fullSnapshotName, volumeType) + resp = forwardedResponseIfVolumeIsRemote(d, r, poolName, projectName, fullSnapshotName, volumeType) if resp != nil { return resp } From lxc-bot at linuxcontainers.org Thu Oct 22 13:51:18 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Thu, 22 Oct 2020 06:51:18 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxc/move: Bypass security.protection.delete Message-ID: <5f918e56.1c69fb81.eda91.7175SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 370 bytes Desc: not available URL: -------------- next part -------------- From 20d1f8c00596b8aa84bfc50a00757be78807718f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 22 Oct 2020 09:50:19 -0400 Subject: [PATCH] lxc/move: Bypass security.protection.delete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #8075 Signed-off-by: Stéphane Graber --- lxc/delete.go | 24 ++++++++++++++++++++++-- lxc/move.go | 1 + 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lxc/delete.go b/lxc/delete.go index 7bc3caf4f0..5aad5dfe4c 100644 --- a/lxc/delete.go +++ b/lxc/delete.go @@ -18,8 +18,9 @@ import ( type cmdDelete struct { global *cmdGlobal - flagForce bool - flagInteractive bool + flagForce bool + flagForceProtected bool + flagInteractive bool } func (c *cmdDelete) Command() *cobra.Command { @@ -125,6 +126,25 @@ func (c *cmdDelete) Run(cmd *cobra.Command, args []string) error { } } + if c.flagForceProtected && shared.IsTrue(ct.ExpandedConfig["security.protection.delete"]) { + // Refresh in case we had to stop it above. + ct, etag, err := resource.server.GetInstance(resource.name) + if err != nil { + return err + } + + ct.Config["security.protection.delete"] = "false" + op, err := resource.server.UpdateInstance(resource.name, ct.Writable(), etag) + if err != nil { + return err + } + + err = op.Wait() + if err != nil { + return err + } + } + if err := c.doDelete(resource.server, resource.name); err != nil { return err } diff --git a/lxc/move.go b/lxc/move.go index 8a1b812976..f77bf6f222 100644 --- a/lxc/move.go +++ b/lxc/move.go @@ -198,6 +198,7 @@ func (c *cmdMove) Run(cmd *cobra.Command, args []string) error { del := cmdDelete{global: c.global} del.flagForce = true + del.flagForceProtected = true err = del.Run(cmd, args[:1]) if err != nil { return errors.Wrap(err, "Failed to delete original instance after copying it") From lxc-bot at linuxcontainers.org Thu Oct 22 15:43:14 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Thu, 22 Oct 2020 08:43:14 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Documentation fixes Message-ID: <5f91a892.1c69fb81.79614.b088SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 914c3d86d7a5266ac726f3ddd2ea8860af449572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 22 Oct 2020 11:40:53 -0400 Subject: [PATCH 1/2] doc/instances: usb and gpu are available in VMs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- doc/instances.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/instances.md b/doc/instances.md index 10e4630a33..9241332089 100644 --- a/doc/instances.md +++ b/doc/instances.md @@ -234,8 +234,8 @@ ID (database) | Name | Condition | Descripti 2 | [disk](#type-disk) | - | Mountpoint inside the instance 3 | [unix-char](#type-unix-char) | container | Unix character device 4 | [unix-block](#type-unix-block) | container | Unix block device -5 | [usb](#type-usb) | container | USB device -6 | [gpu](#type-gpu) | container | GPU device +5 | [usb](#type-usb) | - | USB device +6 | [gpu](#type-gpu) | - | GPU device 7 | [infiniband](#type-infiniband) | container | Infiniband device 8 | [proxy](#type-proxy) | container | Proxy device 9 | [unix-hotplug](#type-unix-hotplug) | container | Unix hotplug device From c415a8f533390a595369687fbedf4b27a555cc4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 22 Oct 2020 11:42:29 -0400 Subject: [PATCH 2/2] doc/instances: Add missing header in usb device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- doc/instances.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/instances.md b/doc/instances.md index 9241332089..399eb6b1c6 100644 --- a/doc/instances.md +++ b/doc/instances.md @@ -727,6 +727,9 @@ mode | int | 0660 | no | Mode of the device in required | boolean | true | no | Whether or not this device is required to start the instance ### Type: usb + +Supported instance types: container, VM + USB device entries simply make the requested USB device appear in the instance. From lxc-bot at linuxcontainers.org Thu Oct 22 19:46:36 2020 From: lxc-bot at linuxcontainers.org (dlemel8 on Github) Date: Thu, 22 Oct 2020 12:46:36 -0700 (PDT) Subject: [lxc-devel] [lxd/master] extract restart logic to new instance interface function of lxc and qemu Message-ID: <5f91e19c.1c69fb81.a0299.0c32SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 11239b22efea6000b56396335373ca94a0f2db83 Mon Sep 17 00:00:00 2001 From: Daniel Segal Date: Thu, 22 Oct 2020 22:45:05 +0300 Subject: [PATCH] extract restart logic to new instance interface function of lxc and qemu Signed-off-by: Daniel Segal --- lxd/instance/drivers/driver_lxc.go | 19 +++++++++++++++++++ lxd/instance/drivers/driver_qemu.go | 19 +++++++++++++++++++ lxd/instance/instance_interface.go | 1 + lxd/instance_state.go | 25 ++++--------------------- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go index ccb8945587..b02928e5c9 100644 --- a/lxd/instance/drivers/driver_lxc.go +++ b/lxd/instance/drivers/driver_lxc.go @@ -2786,6 +2786,25 @@ func (c *lxc) Shutdown(timeout time.Duration) error { return nil } +// Restart restart the instance. +func (c *lxc) Restart(timeout time.Duration) error { + if timeout == 0 { + if err := c.Stop(false); err != nil { + return err + } + } else { + if c.IsFrozen() { + return errors.New("Instance is not running") + } + + if err := c.Shutdown(timeout * time.Second); err != nil { + return err + } + } + + return c.Start(false) +} + // onStopNS is triggered by LXC's stop hook once a container is shutdown but before the container's // namespaces have been closed. The netns path of the stopped container is provided. func (c *lxc) onStopNS(args map[string]string) error { diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index d1436b9263..1a8798ad9d 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -629,6 +629,25 @@ func (vm *qemu) Shutdown(timeout time.Duration) error { return nil } +// Restart restart the instance. +func (vm *qemu) Restart(timeout time.Duration) error { + if timeout == 0 { + if err := vm.Stop(false); err != nil { + return err + } + } else { + if vm.IsFrozen() { + return errors.New("Instance is not running") + } + + if err := vm.Shutdown(timeout * time.Second); err != nil { + return err + } + } + + return vm.Start(false) +} + func (vm *qemu) ovmfPath() string { if os.Getenv("LXD_OVMF_PATH") != "" { return os.Getenv("LXD_OVMF_PATH") diff --git a/lxd/instance/instance_interface.go b/lxd/instance/instance_interface.go index e49458bd86..02f675c227 100644 --- a/lxd/instance/instance_interface.go +++ b/lxd/instance/instance_interface.go @@ -50,6 +50,7 @@ type Instance interface { Shutdown(timeout time.Duration) error Start(stateful bool) error Stop(stateful bool) error + Restart(timeout time.Duration) error Unfreeze() error RegisterDevices() SaveConfigFile() error diff --git a/lxd/instance_state.go b/lxd/instance_state.go index b0fab52b0b..dfcb4b15be 100644 --- a/lxd/instance_state.go +++ b/lxd/instance_state.go @@ -167,28 +167,11 @@ func containerStatePut(d *Daemon, r *http.Request) response.Response { }() } - if raw.Timeout == 0 || raw.Force { - err = c.Stop(false) - if err != nil { - return err - } - } else { - if c.IsFrozen() { - return fmt.Errorf("Instance is not running") - } - - err = c.Shutdown(time.Duration(raw.Timeout) * time.Second) - if err != nil { - return err - } - } - - err = c.Start(false) - if err != nil { - return err + timeout := raw.Timeout + if raw.Force { + timeout = 0 } - - return nil + return c.Restart(time.Duration(timeout)) } case shared.Freeze: if !d.os.CGInfo.Supports(cgroup.Freezer, nil) { From lxc-bot at linuxcontainers.org Thu Oct 22 23:21:54 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Thu, 22 Oct 2020 16:21:54 -0700 (PDT) Subject: [lxc-devel] [lxd/master] scripts/bash: Fix snap handling Message-ID: <5f921412.1c69fb81.9ed6f.20b2SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From 156dc7b463a88fdbb952a4e46e11a74dc5a563b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 22 Oct 2020 19:21:36 -0400 Subject: [PATCH] scripts/bash: Fix snap handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- scripts/bash/lxd-client | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/bash/lxd-client b/scripts/bash/lxd-client index 37769d8ec9..dc84465994 100644 --- a/scripts/bash/lxd-client +++ b/scripts/bash/lxd-client @@ -55,6 +55,10 @@ _have lxc && { return 0 fi + if [ -n "${SNAP_COMMON:-""}" ]; then + export LXD_DIR=${LXD_DIR:-"${SNAP_COMMON}/lxd/"} + fi + lxc_cmds="alias cluster config console copy delete exec export file \ help image import info init launch list manpage monitor move network \ operation pause profile project publish query remote rename \ From lxc-bot at linuxcontainers.org Fri Oct 23 00:02:58 2020 From: lxc-bot at linuxcontainers.org (nutterthanos on Github) Date: Thu, 22 Oct 2020 17:02:58 -0700 (PDT) Subject: [lxc-devel] [lxd-demo-server/master] Update Container to Ubuntu 20.04LTS and lxd-demo-server to core20 Message-ID: <5f921db2.1c69fb81.5e06.8ee3SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 311 bytes Desc: not available URL: -------------- next part -------------- From 61af557b3d3a6e0224464023fe3affb309827c90 Mon Sep 17 00:00:00 2001 From: nutterthanos Date: Fri, 23 Oct 2020 10:30:20 +1030 Subject: [PATCH 1/2] snapcraft: Switch default container to 20.04 --- .snapcraft/lxd-demo.yaml.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.snapcraft/lxd-demo.yaml.tpl b/.snapcraft/lxd-demo.yaml.tpl index 9d6b40a..28f4683 100644 --- a/.snapcraft/lxd-demo.yaml.tpl +++ b/.snapcraft/lxd-demo.yaml.tpl @@ -1,6 +1,6 @@ # Source of the container ## Either an image (local or remote) -image: ubuntu:18.04 +image: ubuntu:20.04 ## Or an existing container #container: some-container From 8f2bb609259b5b3ceba5af56abcfe6ca471d8417 Mon Sep 17 00:00:00 2001 From: nutterthanos Date: Fri, 23 Oct 2020 10:31:26 +1030 Subject: [PATCH 2/2] snapcraft: Switch to core20 --- .snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.snapcraft.yaml b/.snapcraft.yaml index 10e72e6..b5ce1e0 100644 --- a/.snapcraft.yaml +++ b/.snapcraft.yaml @@ -1,5 +1,5 @@ name: lxd-demo-server -base: core18 +base: core20 version: git grade: stable summary: LXD demo server From lxc-bot at linuxcontainers.org Fri Oct 23 09:29:50 2020 From: lxc-bot at linuxcontainers.org (anirudh-goyal on Github) Date: Fri, 23 Oct 2020 02:29:50 -0700 (PDT) Subject: [lxc-devel] [linuxcontainers.org/master] Logic to detect out of date translations #415 Message-ID: <5f92a28e.1c69fb81.32b26.7528SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 1064 bytes Desc: not available URL: -------------- next part -------------- From 688dabfcb3734ffa56abfad8a4d51ff02acee11d Mon Sep 17 00:00:00 2001 From: anirudh-goyal Date: Fri, 23 Oct 2020 14:49:05 +0530 Subject: [PATCH] - Added support for comparing a translated markdown file and its matching source file based on their last modified times. - If the translated file is more than a day older than source file, a warning is displayed with a link to the English version. Signed-off-by: Anirudh Goyal --- generate | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/generate b/generate index 365cf2c..0f104a4 100755 --- a/generate +++ b/generate @@ -214,6 +214,24 @@ def download_sort_key(download_name): # Treat the entire filename as tokens delimited by dash or dot. return [token_key(token) for token in re.split(r'[-.]', download_name)] +def is_translated_md(language_prefix, md_path): + language = language_prefix[1:] # removing the '/' + md_file = md_path.split('/')[-1] # content/cgmanager/contribute.ru.md -> contribute.ru.md + period_split = md_file.split('.') + if len(period_split) > 2 and period_split[1] == language: + return True + return False + +def is_translated_outdated(translated_file_path): + source_file_path = translated_file_path[:-6] + ".md" # cgmanager/contribute.ru.md -> cgmanager/contribute.md + BUFFER = 86400 # 1 day + if (os.path.getmtime(source_file_path) - os.path.getmtime(translated_file_path)) > BUFFER: + return True + return False + +def add_translation_warning(md_content, page_raw_path): + translation_warning = f"### Note: This translated page may be outdated. Please visit the [English version]({page_raw_path}) for the up to date content.\n" + return translation_warning + md_content def gen_page(entry, override, prefix, **variables): item = dict(entry) @@ -259,13 +277,22 @@ def gen_page(entry, override, prefix, **variables): content = fd.read() elif item['generator'] == "markdown": template = "markdown-page.tpl.html" - with open(content_path(item['meta']['input']), "r") as fd: - content = md2html(fd.read()) + md_path = content_path(item['meta']['input']) + with open(md_path, "r") as fd: + md_content = fd.read() + if prefix and is_translated_md(prefix, md_path) and is_translated_outdated(md_path): + md_content = add_translation_warning(md_content, page_raw_path) + content = md2html(md_content) + elif item['generator'] == "downloads": # Support a markdown description before the download table if "input" in item['meta']: - with open(content_path(item['meta']['input']), "r") as fd: - content = md2html(fd.read()) + md_path = content_path(item['meta']['input']) + with open(md_path, "r") as fd: + md_content = fd.read() + if prefix and is_translated_md(prefix, md_path) and is_translated_outdated(md_path): + md_content = add_translation_warning(md_content, page_raw_path) + content = md2html(md_content) downloads = [] download_path = item['meta']['dir'].lstrip("/") From vogel at folz.de Fri Oct 23 13:31:20 2020 From: vogel at folz.de (Robert Vogelgesang) Date: Fri, 23 Oct 2020 15:31:20 +0200 Subject: [lxc-devel] Segfault in lxc-create of lxc 4.0.5 Message-ID: <20201023133120.GA1028@miro.office.folz.de> Hello @all, currently, I'm testing lxc 4.0.5 with multiple Linux distro's, and on some of them lxc-create(1) segfaulted. When I run lxc-create(1) in GDB, I get: Program received signal SIGSEGV, Segmentation fault. x0i0007ffff7b6be79 in do_lxcapi_create (c=c at entry=0x6980b0, t=t at entry=0x7fffffffe874 "vserver", bdevtype=bdevtype at entry=0x0, specs=specs at entry=0x7fffffffe4b0, flags=flags at entry=0, argv=argv at entry=0x7fffffffe640) at lxccontainer.c:1923 1923 bool reset_managed = c->lxc_conf->rootfs.managed; This is near the end of do_lxcapi_create(). A few lines before, the config is reset: /* Reload config to get the rootfs. */ lxc_conf_free(c->lxc_conf); c->lxc_conf = NULL; The segfault happens when recreating the configuration fails afterwards, e. g. when prepend_lxc_header() returns failure. The reason for prepend_lxc_header() failing was a missing call to OpenSSL_add_all_digests(); just before md = EVP_get_digestbyname("sha1"); in src/lxc/utils.c; openssl versions older than 1.1.0 need this. My question now is: What's the correct fix for the segfault? container_destroy() needs a valid configation in c->lxc_conf. Maybe we have to preserve the old c->lxc_conf until the new configuration was created? Best regards, Robert From lxc-bot at linuxcontainers.org Fri Oct 23 22:33:53 2020 From: lxc-bot at linuxcontainers.org (owlnical on Github) Date: Fri, 23 Oct 2020 15:33:53 -0700 (PDT) Subject: [lxc-devel] [linuxcontainers.org/master] changed fedora copr link to lxc4 Message-ID: <5f935a51.1c69fb81.a688e.01a7SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 428 bytes Desc: not available URL: -------------- next part -------------- From 937b81f85776d859bd704456583b7c9ac0179030 Mon Sep 17 00:00:00 2001 From: Fred Uggla Date: Sat, 24 Oct 2020 00:21:25 +0200 Subject: [PATCH] changed fedora copr link to lxc4 Signed-off-by: Fred Uggla --- content/lxd/getting-started-cli.ja.md | 4 ++-- content/lxd/getting-started-cli.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/content/lxd/getting-started-cli.ja.md b/content/lxd/getting-started-cli.ja.md index 9f8cfc7..b17391d 100644 --- a/content/lxd/getting-started-cli.ja.md +++ b/content/lxd/getting-started-cli.ja.md @@ -86,9 +86,9 @@ Alternatively, the snap package can also be used on Arch Linux ([see below](#sna #### Fedora -LXD の COPR リポジトリの使い方については [こちら](https://copr.fedorainfracloud.org/coprs/ganto/lxc3/) をご覧ください。 +LXD の COPR リポジトリの使い方については [こちら](https://copr.fedorainfracloud.org/coprs/ganto/lxc4/) をご覧ください。 - pam_cgfs.so モジュールは、cgroupfs v1 (/sys/fs/cgroup/$controller) と cgroupfs v2 (/sys/fs/cgroup) のいずれも扱えます。また、コントローラのいくつかが cgroupfs v1 ツリー (/sys/fs/cgroup/$controller) で、それ以外が cgroupfs v2 (/sys/fs/cgroup/unified) ツリーと言ったようなミックスでも扱えます。 + for only controllers listed as arguments on the command line. + Pure cgroup v2 mount is not covered by the pam_cgfs.so module. + --> + pam_cgfs.so モジュールは、純粋な cgroupfs v1 (/sys/fs/cgroup/$controller) ツリーと、コントローラのいくつかが cgroupfs v1 ツリー (/sys/fs/cgroup/$controller) で、それ以外が cgroupfs v2 (/sys/fs/cgroup/unified) ツリーと言ったようなミックスマウントを扱えます。 書き込み可能な cgroup がすべてのコントローラ用に作られます。また、引数で指定すれば、指定したコントローラのみ書き込み可能な cgroup が作られます。 + 純粋な cgroup v2 のみのマウントは pam_cgfs.so モジュールでは対象外です。 From noreply at github.com Sat Oct 24 17:59:23 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Sat, 24 Oct 2020 10:59:23 -0700 Subject: [lxc-devel] [lxc/lxc] bf7368: Update Japanese pam_cgfs(8) to reflect lack of sup... Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: bf73687ae5ab99335390c6817ca91bc7c0793880 https://github.com/lxc/lxc/commit/bf73687ae5ab99335390c6817ca91bc7c0793880 Author: KATOH Yasufumi Date: 2020-10-25 (Sun, 25 Oct 2020) Changed paths: M doc/ja/pam_cgfs.sgml.in Log Message: ----------- Update Japanese pam_cgfs(8) to reflect lack of support for pure cgroupv2 Update for commit b87ed83bbc7db3f826b4f54df1bb458c2c539be7 Signed-off-by: KATOH Yasufumi Commit: c8fe11552a41bd1abdc423dd2489539bc324afd9 https://github.com/lxc/lxc/commit/c8fe11552a41bd1abdc423dd2489539bc324afd9 Author: Stéphane Graber Date: 2020-10-24 (Sat, 24 Oct 2020) Changed paths: M doc/ja/pam_cgfs.sgml.in Log Message: ----------- Merge pull request #3561 from tenforward/japanese Update Japanese pam_cgfs(8) to reflect lack of support for pure cgroupv2 Compare: https://github.com/lxc/lxc/compare/c639f45ee56b...c8fe11552a41 From builds at travis-ci.org Sat Oct 24 18:18:12 2020 From: builds at travis-ci.org (Travis CI) Date: Sat, 24 Oct 2020 18:18:12 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7917 (master - c8fe115) In-Reply-To: Message-ID: <5f946fe3cd644_13fc129cbf2bc6198b@travis-tasks-766987568c-tvgsv.mail> Build Update for lxc/lxc ------------------------------------- Build: #7917 Status: Errored Duration: 18 mins and 48 secs Commit: c8fe115 (master) Author: Stéphane Graber Message: Merge pull request #3561 from tenforward/japanese Update Japanese pam_cgfs(8) to reflect lack of support for pure cgroupv2 View the changeset: https://github.com/lxc/lxc/compare/c639f45ee56b...c8fe11552a41 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/738591495?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lxc-bot at linuxcontainers.org Sun Oct 25 15:39:18 2020 From: lxc-bot at linuxcontainers.org (lxc-jp on Github) Date: Sun, 25 Oct 2020 08:39:18 -0700 (PDT) Subject: [lxc-devel] [linuxcontainers.org/master] Add Japanese release announcement of LXCFS 4.0.6 Message-ID: <5f959c26.1c69fb81.41ba3.7316SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 370 bytes Desc: not available URL: -------------- next part -------------- From 3000ef49238e4a9024dfcbff996937b1287a1b6d Mon Sep 17 00:00:00 2001 From: KATOH Yasufumi Date: Mon, 26 Oct 2020 00:37:46 +0900 Subject: [PATCH] Add Japanese release announcement of LXCFS 4.0.6 Signed-off-by: KATOH Yasufumi --- content/lxcfs/news.ja/lxcfs-4.0.6.yaml | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 content/lxcfs/news.ja/lxcfs-4.0.6.yaml diff --git a/content/lxcfs/news.ja/lxcfs-4.0.6.yaml b/content/lxcfs/news.ja/lxcfs-4.0.6.yaml new file mode 100644 index 0000000..883eb9e --- /dev/null +++ b/content/lxcfs/news.ja/lxcfs-4.0.6.yaml @@ -0,0 +1,50 @@ +title: LXCFS 4.0.6 LTS リリースのお知らせ +date: 2020/10/19 22:10 +origin: https://discuss.linuxcontainers.org/t/lxcfs-4-0-6-lts-has-been-released/9236 +content: |- + ### はじめに + + LXCFS チームが LXCFS 4.0.6 のリリースをお知らせします! + + + これは 2025 年 6 月までサポートされる LXCFS 4.0 の 6 回目のバグフィックスリリースです。 + + ### バグ修正 + + このリリースで対応、修正された主な項目は次のとおりです: + + - fuse3 サポートの追加 + - 新しいカーネルの `diskstats` フィールドに対する更新 + - いくつかのビルドの問題を修正 + + + コミットの全リストは次の通りです(翻訳なし): + + - fix epoll create socket error message + - Add support for fuse3 + - Fix `get_min_memlimit()` on non-glibc + - Update options passed to fuse_main + - rename fuse_compat.h to avoid conflict with system header + - Set the file size to 4k + - diskstats: support new fields in 4.18+ kernels + + ### サポートとアップグレード + + LXCFS 4.0 ブランチは 2025 年 6 月までサポートされます。 + stable のバグフィックスリリースでは、バグとセキュリティに関する問題に対する修正のみが行われますので、常に安全です。最新のバグフィックスリリースの状態を維持し、実行することをおすすめします。 + + ### ダウンロード + + - リリース tarball : [lxcfs-4.0.6.tar.gz](https://linuxcontainers.org/downloads/lxcfs/lxcfs-4.0.6.tar.gz) + - GPG シグネチャー : [lxcfs-4.0.6.tar.gz.asc](https://linuxcontainers.org/downloads/lxcfs/lxcfs-4.0.6.tar.gz.asc) From lxc-bot at linuxcontainers.org Mon Oct 26 10:37:17 2020 From: lxc-bot at linuxcontainers.org (tenforward on Github) Date: Mon, 26 Oct 2020 03:37:17 -0700 (PDT) Subject: [lxc-devel] [lxc-ci/master] images/plamo: fix for current Plamo 7.x Message-ID: <5f96a6dd.1c69fb81.423e8.219cSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 471 bytes Desc: not available URL: -------------- next part -------------- From eaa1f53c31f93b2a1be1442b6c4c9738daaaf612 Mon Sep 17 00:00:00 2001 From: KATOH Yasufumi Date: Mon, 26 Oct 2020 17:10:15 +0900 Subject: [PATCH] images/plamo: fix for current Plamo 7.x This is the equivalent of https://github.com/lxc/lxc-templates/commit/551f56b5d529ced1b6b51206c4e9b7055e338028 Signed-off-by: KATOH Yasufumi --- images/plamo.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/images/plamo.yaml b/images/plamo.yaml index fa98f4c..02475eb 100644 --- a/images/plamo.yaml +++ b/images/plamo.yaml @@ -322,14 +322,11 @@ actions: sed -i -e '/wait_for_user/d' -e '/Press Enter to/d' /etc/rc.d/init.d/rc fi - # initpkg - for pkg in shadow netconfig7 eudev openssh; do + # remove initpkg that do not execute on containers + for pkg in shadow netconfig7 eudev openssh pkgtools7; do rm -f "/var/log/initpkg/${pkg}" done - - for pkg in /var/log/initpkg/*; do - sh ${pkg} - done + ( cd /sbin ; mv installer_new installer ) mappings: architecture_map: plamolinux From lxc-bot at linuxcontainers.org Mon Oct 26 23:08:19 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Mon, 26 Oct 2020 16:08:19 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Ensure consistent container copies on dir/ceph Message-ID: <5f9756e3.1c69fb81.406b1.0ff6SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From d5fe43aa567c9684bedffc051478f17ee8b11d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 26 Oct 2020 19:07:40 -0400 Subject: [PATCH 1/2] lxd/storage: Rename RunningSnapshotFreeze to RunningCopyFreeze MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/storage/backend_lxd.go | 3 ++- lxd/storage/drivers/driver_btrfs.go | 2 +- lxd/storage/drivers/driver_ceph.go | 22 +++++++++++----------- lxd/storage/drivers/driver_cephfs.go | 22 +++++++++++----------- lxd/storage/drivers/driver_dir.go | 22 +++++++++++----------- lxd/storage/drivers/driver_lvm.go | 22 +++++++++++----------- lxd/storage/drivers/driver_types.go | 2 +- lxd/storage/drivers/driver_zfs.go | 24 ++++++++++++------------ lxd/storage/drivers/drivers_mock.go | 22 +++++++++++----------- 9 files changed, 71 insertions(+), 70 deletions(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index e14f911692..4a6aa5619f 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -1709,11 +1709,12 @@ func (b *lxdBackend) CreateInstanceSnapshot(inst instance.Instance, src instance } // Some driver backing stores require that running instances be frozen during snapshot. - if b.driver.Info().RunningSnapshotFreeze && src.IsRunning() { + if b.driver.Info().RunningCopyFreeze && src.IsRunning() { err = src.Freeze() if err != nil { return err } + defer src.Unfreeze() } diff --git a/lxd/storage/drivers/driver_btrfs.go b/lxd/storage/drivers/driver_btrfs.go index c98b8409de..bbf1dd6280 100644 --- a/lxd/storage/drivers/driver_btrfs.go +++ b/lxd/storage/drivers/driver_btrfs.go @@ -81,7 +81,7 @@ func (d *btrfs) Info() Info { VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM}, BlockBacking: false, RunningQuotaResize: true, - RunningSnapshotFreeze: false, + RunningCopyFreeze: false, DirectIO: true, MountedRoot: true, } diff --git a/lxd/storage/drivers/driver_ceph.go b/lxd/storage/drivers/driver_ceph.go index 2500bb94ac..111d44239c 100644 --- a/lxd/storage/drivers/driver_ceph.go +++ b/lxd/storage/drivers/driver_ceph.go @@ -72,17 +72,17 @@ func (d *ceph) isRemote() bool { // Info returns info about the driver and its environment. func (d *ceph) Info() Info { return Info{ - Name: "ceph", - Version: cephVersion, - OptimizedImages: true, - PreservesInodes: false, - Remote: d.isRemote(), - VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM}, - BlockBacking: true, - RunningQuotaResize: false, - RunningSnapshotFreeze: true, - DirectIO: true, - MountedRoot: false, + Name: "ceph", + Version: cephVersion, + OptimizedImages: true, + PreservesInodes: false, + Remote: d.isRemote(), + VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM}, + BlockBacking: true, + RunningQuotaResize: false, + RunningCopyFreeze: true, + DirectIO: true, + MountedRoot: false, } } diff --git a/lxd/storage/drivers/driver_cephfs.go b/lxd/storage/drivers/driver_cephfs.go index 015da2c949..b1c39ea348 100644 --- a/lxd/storage/drivers/driver_cephfs.go +++ b/lxd/storage/drivers/driver_cephfs.go @@ -71,17 +71,17 @@ func (d *cephfs) isRemote() bool { // Info returns the pool driver information. func (d *cephfs) Info() Info { return Info{ - Name: "cephfs", - Version: cephfsVersion, - OptimizedImages: false, - PreservesInodes: false, - Remote: d.isRemote(), - VolumeTypes: []VolumeType{VolumeTypeCustom}, - BlockBacking: false, - RunningQuotaResize: true, - RunningSnapshotFreeze: false, - DirectIO: true, - MountedRoot: true, + Name: "cephfs", + Version: cephfsVersion, + OptimizedImages: false, + PreservesInodes: false, + Remote: d.isRemote(), + VolumeTypes: []VolumeType{VolumeTypeCustom}, + BlockBacking: false, + RunningQuotaResize: true, + RunningCopyFreeze: false, + DirectIO: true, + MountedRoot: true, } } diff --git a/lxd/storage/drivers/driver_dir.go b/lxd/storage/drivers/driver_dir.go index 906024f17b..df9023823e 100644 --- a/lxd/storage/drivers/driver_dir.go +++ b/lxd/storage/drivers/driver_dir.go @@ -34,17 +34,17 @@ func (d *dir) load() error { // Info returns info about the driver and its environment. func (d *dir) Info() Info { return Info{ - Name: "dir", - Version: "1", - OptimizedImages: false, - PreservesInodes: false, - Remote: d.isRemote(), - VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM}, - BlockBacking: false, - RunningQuotaResize: true, - RunningSnapshotFreeze: true, - DirectIO: true, - MountedRoot: true, + Name: "dir", + Version: "1", + OptimizedImages: false, + PreservesInodes: false, + Remote: d.isRemote(), + VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM}, + BlockBacking: false, + RunningQuotaResize: true, + RunningCopyFreeze: true, + DirectIO: true, + MountedRoot: true, } } diff --git a/lxd/storage/drivers/driver_lvm.go b/lxd/storage/drivers/driver_lvm.go index cda510284f..75fe9f0476 100644 --- a/lxd/storage/drivers/driver_lvm.go +++ b/lxd/storage/drivers/driver_lvm.go @@ -87,17 +87,17 @@ func (d *lvm) load() error { // Info returns info about the driver and its environment. func (d *lvm) Info() Info { return Info{ - Name: "lvm", - Version: lvmVersion, - OptimizedImages: d.usesThinpool(), // Only thinpool pools support optimized images. - PreservesInodes: false, - Remote: d.isRemote(), - VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM}, - BlockBacking: true, - RunningQuotaResize: false, - RunningSnapshotFreeze: false, - DirectIO: true, - MountedRoot: false, + Name: "lvm", + Version: lvmVersion, + OptimizedImages: d.usesThinpool(), // Only thinpool pools support optimized images. + PreservesInodes: false, + Remote: d.isRemote(), + VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM}, + BlockBacking: true, + RunningQuotaResize: false, + RunningCopyFreeze: false, + DirectIO: true, + MountedRoot: false, } } diff --git a/lxd/storage/drivers/driver_types.go b/lxd/storage/drivers/driver_types.go index bba7d4e01d..dbfd222a92 100644 --- a/lxd/storage/drivers/driver_types.go +++ b/lxd/storage/drivers/driver_types.go @@ -12,7 +12,7 @@ type Info struct { PreservesInodes bool // Whether driver preserves inodes when volumes are moved hosts. BlockBacking bool // Whether driver uses block devices as backing store. RunningQuotaResize bool // Whether quota resize is supported whilst instance running. - RunningSnapshotFreeze bool // Whether instance should be frozen during snapshot if running. + RunningCopyFreeze bool // Whether instance should be frozen during snapshot if running. DirectIO bool // Whether the driver supports direct I/O. MountedRoot bool // Whether the pool directory itself is a mount. } diff --git a/lxd/storage/drivers/driver_zfs.go b/lxd/storage/drivers/driver_zfs.go index 5e290af54f..956f8297fb 100644 --- a/lxd/storage/drivers/driver_zfs.go +++ b/lxd/storage/drivers/driver_zfs.go @@ -103,18 +103,18 @@ func (d *zfs) load() error { // Info returns info about the driver and its environment. func (d *zfs) Info() Info { info := Info{ - Name: "zfs", - Version: zfsVersion, - OptimizedImages: true, - OptimizedBackups: true, - PreservesInodes: true, - Remote: d.isRemote(), - VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM}, - BlockBacking: false, - RunningQuotaResize: true, - RunningSnapshotFreeze: false, - DirectIO: zfsDirectIO, - MountedRoot: false, + Name: "zfs", + Version: zfsVersion, + OptimizedImages: true, + OptimizedBackups: true, + PreservesInodes: true, + Remote: d.isRemote(), + VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM}, + BlockBacking: false, + RunningQuotaResize: true, + RunningCopyFreeze: false, + DirectIO: zfsDirectIO, + MountedRoot: false, } return info diff --git a/lxd/storage/drivers/drivers_mock.go b/lxd/storage/drivers/drivers_mock.go index bc876b075d..b3967e5970 100644 --- a/lxd/storage/drivers/drivers_mock.go +++ b/lxd/storage/drivers/drivers_mock.go @@ -22,17 +22,17 @@ func (d *mock) load() error { // Info returns info about the driver and its environment. func (d *mock) Info() Info { return Info{ - Name: "mock", - Version: "1", - OptimizedImages: false, - PreservesInodes: false, - Remote: d.isRemote(), - VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM}, - BlockBacking: false, - RunningQuotaResize: true, - RunningSnapshotFreeze: true, - DirectIO: true, - MountedRoot: true, + Name: "mock", + Version: "1", + OptimizedImages: false, + PreservesInodes: false, + Remote: d.isRemote(), + VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM}, + BlockBacking: false, + RunningQuotaResize: true, + RunningCopyFreeze: true, + DirectIO: true, + MountedRoot: true, } } From 1cb7475c22a02f5317641c746c87430b0d561551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 26 Oct 2020 19:07:52 -0400 Subject: [PATCH 2/2] lxd/storage: Ensure source is frozen during copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/storage/backend_lxd.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index 4a6aa5619f..b2c1272991 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -673,6 +673,16 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance return err } + // Some driver backing stores require that running instances be frozen during copy. + if !src.IsSnapshot() && b.driver.Info().RunningCopyFreeze && src.IsRunning() { + err = src.Freeze() + if err != nil { + return err + } + + defer src.Unfreeze() + } + if b.Name() == srcPool.Name() { logger.Debug("CreateInstanceFromCopy same-pool mode detected") err = b.driver.CreateVolumeFromCopy(vol, srcVol, snapshots, op) From lxc-bot at linuxcontainers.org Tue Oct 27 08:21:56 2020 From: lxc-bot at linuxcontainers.org (Drachenfels-GmbH on Github) Date: Tue, 27 Oct 2020 01:21:56 -0700 (PDT) Subject: [lxc-devel] [lxc/master] seccomp: fix pseudo syscalls, improve logging and avoid duplicate processing Message-ID: <5f97d8a4.1c69fb81.8c9da.1b0fSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 6445 bytes Desc: not available URL: -------------- next part -------------- From 0ff0d23e4001ec9cadae51b41e834a954ef5026c Mon Sep 17 00:00:00 2001 From: Ruben Jenster Date: Thu, 22 Oct 2020 17:15:58 +0200 Subject: [PATCH 1/2] seccomp: Fix handling of pseudo syscalls and improve logging for rule processing. Signed-off-by: Ruben Jenster --- src/lxc/seccomp.c | 74 +++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c index 03c1b54f01..f97e5cb86d 100644 --- a/src/lxc/seccomp.c +++ b/src/lxc/seccomp.c @@ -486,7 +486,14 @@ static scmp_filter_ctx get_new_ctx(enum lxc_hostarch_t n_arch, uint32_t default_ return ctx; } -static bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx, +enum lxc_seccomp_rule_status_t { + lxc_seccomp_rule_added = 0, + lxc_seccomp_rule_err, + lxc_seccomp_rule_undefined_syscall, + lxc_seccomp_rule_unsupported_arch, +}; + +static enum lxc_seccomp_rule_status_t do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx, struct seccomp_v2_rule *rule) { int i, nr, ret; @@ -496,7 +503,7 @@ static bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx, if (arch && ret != 0) { errno = -ret; SYSERROR("Seccomp: rule and context arch do not match (arch %d)", arch); - return false; + return lxc_seccomp_rule_err; } /*get the syscall name*/ @@ -511,29 +518,28 @@ static bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx, if (ret < 0) { errno = -ret; SYSERROR("Failed loading rule to reject force umount"); - return false; + return lxc_seccomp_rule_err; } INFO("Set seccomp rule to reject force umounts"); - return true; + return lxc_seccomp_rule_added; } nr = seccomp_syscall_resolve_name(line); if (nr == __NR_SCMP_ERROR) { - WARN("Failed to resolve syscall \"%s\"", line); - WARN("This syscall will NOT be handled by seccomp"); - return true; + INFO("The syscall[%s] is is undefined on host native arch", line); + return lxc_seccomp_rule_undefined_syscall; } - if (nr < 0) { - WARN("Got negative return value %d for syscall \"%s\"", nr, line); - WARN("This syscall will NOT be handled by seccomp"); - return true; + // The syscall resolves to a pseudo syscall and may be available on compat archs. + if (nr < 0 && arch == SCMP_ARCH_NATIVE) { + DEBUG("The syscall[%d:%s] is a pseudo syscall and not available on host native arch.", nr, line); + return lxc_seccomp_rule_unsupported_arch; } if (arch != SCMP_ARCH_NATIVE && seccomp_syscall_resolve_name_arch(arch, line) < 0) { - INFO("The syscall \"%s\" nr:%d is not supported on compat arch:%d", line, nr, arch); - return true; + DEBUG("The syscall[%d:%s] is not supported on compat arch[%u]", nr, line, arch); + return lxc_seccomp_rule_unsupported_arch; } memset(&arg_cmp, 0, sizeof(arg_cmp)); @@ -555,16 +561,20 @@ static bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx, rule->args_value[i].value); } + INFO("Adding %s rule for syscall[%d:%s] action[%d:%s] arch[%u]", + (arch == SCMP_ARCH_NATIVE) ? "native" : "compat", + nr, line, rule->action, get_action_name(rule->action), arch); + ret = seccomp_rule_add_exact_array(ctx, rule->action, nr, rule->args_num, arg_cmp); if (ret < 0) { errno = -ret; - SYSERROR("Failed loading rule for %s (nr %d action %d (%s))", - line, nr, rule->action, get_action_name(rule->action)); - return false; + SYSERROR("Failed to add rule for syscall[%d:%s] action[%d:%s] arch[%u]", + nr, line, rule->action, get_action_name(rule->action), arch); + return lxc_seccomp_rule_err; } - return true; + return lxc_seccomp_rule_added; } /* @@ -983,42 +993,30 @@ static int parse_config_v2(FILE *f, char *line, size_t *line_bufsz, struct lxc_c } #endif - if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, - conf->seccomp.seccomp_ctx, &rule)) - goto bad_rule; - INFO("Added native rule for arch %d for %s action %d(%s)", - SCMP_ARCH_NATIVE, line, rule.action, - get_action_name(rule.action)); + ret = do_resolve_add_rule(SCMP_ARCH_NATIVE, line, + conf->seccomp.seccomp_ctx, &rule); + if (ret == lxc_seccomp_rule_err) + goto bad_rule; + if (ret == lxc_seccomp_rule_undefined_syscall) + continue; if (ctx.architectures[0] != SCMP_ARCH_NATIVE) { - if (!do_resolve_add_rule(ctx.architectures[0], line, + if (lxc_seccomp_rule_err == do_resolve_add_rule(ctx.architectures[0], line, ctx.contexts[0], &rule)) goto bad_rule; - - INFO("Added compat rule for arch %d for %s action %d(%s)", - ctx.architectures[0], line, rule.action, - get_action_name(rule.action)); } if (ctx.architectures[1] != SCMP_ARCH_NATIVE) { - if (!do_resolve_add_rule(ctx.architectures[1], line, + if (lxc_seccomp_rule_err == do_resolve_add_rule(ctx.architectures[1], line, ctx.contexts[1], &rule)) goto bad_rule; - - INFO("Added compat rule for arch %d for %s action %d(%s)", - ctx.architectures[1], line, rule.action, - get_action_name(rule.action)); } if (ctx.architectures[2] != SCMP_ARCH_NATIVE) { - if (!do_resolve_add_rule(ctx.architectures[2], line, + if (lxc_seccomp_rule_err == do_resolve_add_rule(ctx.architectures[2], line, ctx.contexts[2], &rule)) goto bad_rule; - - INFO("Added native rule for arch %d for %s action %d(%s)", - ctx.architectures[2], line, rule.action, - get_action_name(rule.action)); } } From 15044cd19c8454b20ee46fdb17dd0c8dd85366b1 Mon Sep 17 00:00:00 2001 From: Ruben Jenster Date: Fri, 23 Oct 2020 16:03:12 +0200 Subject: [PATCH 2/2] seccomp: Avoid duplicate processing of rules for host native arch. Signed-off-by: Ruben Jenster --- src/lxc/seccomp.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c index f97e5cb86d..4faf693f6c 100644 --- a/src/lxc/seccomp.c +++ b/src/lxc/seccomp.c @@ -653,6 +653,8 @@ static int parse_config_v2(FILE *f, char *line, size_t *line_bufsz, struct lxc_c default_rule_action = SCMP_ACT_ALLOW; } + DEBUG("Host native arch is [%u]", seccomp_arch_native()); + memset(&ctx, 0, sizeof(ctx)); ctx.architectures[0] = SCMP_ARCH_NATIVE; ctx.architectures[1] = SCMP_ARCH_NATIVE; @@ -1001,23 +1003,15 @@ static int parse_config_v2(FILE *f, char *line, size_t *line_bufsz, struct lxc_c if (ret == lxc_seccomp_rule_undefined_syscall) continue; - if (ctx.architectures[0] != SCMP_ARCH_NATIVE) { - if (lxc_seccomp_rule_err == do_resolve_add_rule(ctx.architectures[0], line, - ctx.contexts[0], &rule)) - goto bad_rule; - } - - if (ctx.architectures[1] != SCMP_ARCH_NATIVE) { - if (lxc_seccomp_rule_err == do_resolve_add_rule(ctx.architectures[1], line, - ctx.contexts[1], &rule)) - goto bad_rule; + for (int i = 0; i < 3; i++ ) { + uint32_t arch = ctx.architectures[i]; + if (arch != SCMP_ARCH_NATIVE && arch != seccomp_arch_native()) { + if (lxc_seccomp_rule_err == do_resolve_add_rule(arch, line, + ctx.contexts[i], &rule)) + goto bad_rule; + } } - if (ctx.architectures[2] != SCMP_ARCH_NATIVE) { - if (lxc_seccomp_rule_err == do_resolve_add_rule(ctx.architectures[2], line, - ctx.contexts[2], &rule)) - goto bad_rule; - } } INFO("Merging compat seccomp contexts into main context"); From lxc-bot at linuxcontainers.org Tue Oct 27 08:40:31 2020 From: lxc-bot at linuxcontainers.org (Drachenfels-GmbH on Github) Date: Tue, 27 Oct 2020 01:40:31 -0700 (PDT) Subject: [lxc-devel] [lxc/master] cgroups: Introduce lxc.cgroup.dir.monitor.pivot - fixes cgroup removal on termination Message-ID: <5f97dcff.1c69fb81.aea66.b89bSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 1836 bytes Desc: not available URL: -------------- next part -------------- From 7696c1f9d1aed98a54bf7acd4c48799c395cdc64 Mon Sep 17 00:00:00 2001 From: Ruben Jenster Date: Fri, 23 Oct 2020 11:33:38 +0200 Subject: [PATCH] Introduce lxc.cgroup.dir.monitor.pivot On termination lxc may fail to remove either lxc.cgroup.dir or lxc.cgroup.dir.monitor, because the monitor process may still be a member of either of these cgroups. The pivot cgroup should not be a member (subpath) of any other container cgroup (dir). because only empty cgroups can be removed. Signed-off-by: Ruben Jenster --- doc/lxc.container.conf.sgml.in | 12 ++++++++++++ src/lxc/cgroups/cgfsng.c | 5 ++++- src/lxc/conf.h | 1 + src/lxc/confile.c | 36 ++++++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in index ba25b34130..ac724cebe5 100644 --- a/doc/lxc.container.conf.sgml.in +++ b/doc/lxc.container.conf.sgml.in @@ -1604,6 +1604,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + + + + + + On container termination the PID of the monitor process is attached to this cgroup. + This path should not be a subpath of any other configured cgroup dir to ensure + proper removal of other cgroup paths on container termination. + + + diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c index f508c63d36..a699a4445f 100644 --- a/src/lxc/cgroups/cgfsng.c +++ b/src/lxc/cgroups/cgfsng.c @@ -1093,7 +1093,10 @@ __cgfsng_ops static void cgfsng_monitor_destroy(struct cgroup_ops *ops, goto try_lxc_rm_rf; } - if (conf && conf->cgroup_meta.monitor_dir) + if (conf && conf->cgroup_meta.monitor_pivot_dir) + pivot_path = must_make_path(h->mountpoint, h->container_base_path, + conf->cgroup_meta.monitor_pivot_dir, CGROUP_PIVOT, NULL); + else if (conf && conf->cgroup_meta.monitor_dir) pivot_path = must_make_path(h->mountpoint, h->container_base_path, conf->cgroup_meta.monitor_dir, CGROUP_PIVOT, NULL); else if (conf && conf->cgroup_meta.dir) diff --git a/src/lxc/conf.h b/src/lxc/conf.h index ba06d42dc0..907cbdfa52 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -61,6 +61,7 @@ struct lxc_cgroup { char *controllers; char *dir; char *monitor_dir; + char *monitor_pivot_dir; char *container_dir; char *namespace_dir; bool relative; diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 75587d0ac8..205b980136 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -73,6 +73,7 @@ lxc_config_define(cgroup_controller); lxc_config_define(cgroup2_controller); lxc_config_define(cgroup_dir); lxc_config_define(cgroup_monitor_dir); +lxc_config_define(cgroup_monitor_pivot_dir); lxc_config_define(cgroup_container_dir); lxc_config_define(cgroup_container_inner_dir); lxc_config_define(cgroup_relative); @@ -178,6 +179,7 @@ static struct lxc_config_t config_jump_table[] = { { "lxc.cap.drop", set_config_cap_drop, get_config_cap_drop, clr_config_cap_drop, }, { "lxc.cap.keep", set_config_cap_keep, get_config_cap_keep, clr_config_cap_keep, }, { "lxc.cgroup2", set_config_cgroup2_controller, get_config_cgroup2_controller, clr_config_cgroup2_controller, }, + { "lxc.cgroup.dir.monitor.pivot", set_config_cgroup_monitor_pivot_dir, get_config_cgroup_monitor_pivot_dir, clr_config_cgroup_monitor_pivot_dir, }, { "lxc.cgroup.dir.monitor", set_config_cgroup_monitor_dir, get_config_cgroup_monitor_dir, clr_config_cgroup_monitor_dir, }, { "lxc.cgroup.dir.container.inner", set_config_cgroup_container_inner_dir, get_config_cgroup_container_inner_dir, clr_config_cgroup_container_inner_dir, }, { "lxc.cgroup.dir.container", set_config_cgroup_container_dir, get_config_cgroup_container_dir, clr_config_cgroup_container_dir, }, @@ -1814,6 +1816,16 @@ static int set_config_cgroup_monitor_dir(const char *key, const char *value, value); } +static int set_config_cgroup_monitor_pivot_dir(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + if (lxc_config_value_empty(value)) + return clr_config_cgroup_monitor_pivot_dir(key, lxc_conf, NULL); + + return set_config_string_item(&lxc_conf->cgroup_meta.monitor_pivot_dir, + value); +} + static int set_config_cgroup_container_dir(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) @@ -3858,6 +3870,22 @@ static int get_config_cgroup_monitor_dir(const char *key, char *retv, int inlen, return fulllen; } +static int get_config_cgroup_monitor_pivot_dir(const char *key, char *retv, int inlen, + struct lxc_conf *lxc_conf, void *data) +{ + int len; + int fulllen = 0; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + strprint(retv, inlen, "%s", lxc_conf->cgroup_meta.monitor_pivot_dir); + + return fulllen; +} + static int get_config_cgroup_container_dir(const char *key, char *retv, int inlen, struct lxc_conf *lxc_conf, @@ -4756,6 +4784,14 @@ static int clr_config_cgroup_monitor_dir(const char *key, return 0; } +static int clr_config_cgroup_monitor_pivot_dir(const char *key, + struct lxc_conf *lxc_conf, + void *data) +{ + free_disarm(lxc_conf->cgroup_meta.monitor_pivot_dir); + return 0; +} + static int clr_config_cgroup_container_dir(const char *key, struct lxc_conf *lxc_conf, void *data) From lxc-bot at linuxcontainers.org Tue Oct 27 08:55:52 2020 From: lxc-bot at linuxcontainers.org (Drachenfels-GmbH on Github) Date: Tue, 27 Oct 2020 01:55:52 -0700 (PDT) Subject: [lxc-devel] [lxc/master] lxccontainer: fix lxc_config_item_is_supported Message-ID: <5f97e098.1c69fb81.c8f22.3cd0SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 753 bytes Desc: not available URL: -------------- next part -------------- From 6eb516a793edd7c8e37472d00d1fc599f176bb97 Mon Sep 17 00:00:00 2001 From: Ruben Jenster Date: Fri, 23 Oct 2020 18:32:15 +0200 Subject: [PATCH] lxccontainer: fix lxc_config_item_is_supported Use exact match instead of longest prefix match to check whether a config item is supported. Signed-off-by: Ruben Jenster --- src/lxc/confile.c | 12 ++++++++++++ src/lxc/confile.h | 3 +++ src/lxc/lxccontainer.c | 2 +- src/tests/get_item.c | 5 +++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 75587d0ac8..08dd691667 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -278,6 +278,18 @@ static struct lxc_config_t config_jump_table[] = { static const size_t config_jump_table_size = sizeof(config_jump_table) / sizeof(struct lxc_config_t); +struct lxc_config_t *lxc_get_config_exact(const char *key) +{ + size_t i; + + for (i = 0; i < config_jump_table_size; i++) + if (!strcmp(config_jump_table[i].name, key)) + return &config_jump_table[i]; + + return NULL; +} + + struct lxc_config_t *lxc_get_config(const char *key) { size_t i; diff --git a/src/lxc/confile.h b/src/lxc/confile.h index df80f639a3..68d50fc804 100644 --- a/src/lxc/confile.h +++ b/src/lxc/confile.h @@ -45,6 +45,9 @@ struct new_config_item { }; /* Get the jump table entry for the given configuration key. */ +__hidden extern struct lxc_config_t *lxc_get_config_exact(const char *key); + +/* Get the jump table entry if entry name is a prefix of the given configuration key. */ __hidden extern struct lxc_config_t *lxc_get_config(const char *key); /* List all available config items. */ diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 673cf2483d..96aa372e1d 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -5749,7 +5749,7 @@ int list_all_containers(const char *lxcpath, char ***nret, bool lxc_config_item_is_supported(const char *key) { - return !!lxc_get_config(key); + return !!lxc_get_config_exact(key); } bool lxc_has_api_extension(const char *extension) diff --git a/src/tests/get_item.c b/src/tests/get_item.c index f2757c29d8..11db5f6738 100644 --- a/src/tests/get_item.c +++ b/src/tests/get_item.c @@ -600,6 +600,11 @@ int main(int argc, char *argv[]) goto out; } + if (lxc_config_item_is_supported("lxc.arch.nonsense")) { + fprintf(stderr, "%d: failed to detect \"lxc.arch.nonsense\" as unsupported configuration item\n", __LINE__); + goto out; + } + if (c->set_config_item(c, "lxc.notaconfigkey", "invalid")) { fprintf(stderr, "%d: Managed to set \"lxc.notaconfigkey\"\n", __LINE__); goto out; From lxc-bot at linuxcontainers.org Tue Oct 27 08:58:48 2020 From: lxc-bot at linuxcontainers.org (Drachenfels-GmbH on Github) Date: Tue, 27 Oct 2020 01:58:48 -0700 (PDT) Subject: [lxc-devel] [lxc/master] tests: Fix compilation with appamor enabled. Message-ID: <5f97e148.1c69fb81.25d6a.3b83SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 1998 bytes Desc: not available URL: -------------- next part -------------- From beff99393906f3bddff186993203a8b9c0ba9dfc Mon Sep 17 00:00:00 2001 From: Ruben Jenster Date: Fri, 23 Oct 2020 18:26:34 +0200 Subject: [PATCH] tests: Fix compilation with appamor enabled. Signed-off-by: Ruben Jenster --- src/tests/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 2b66bc9cd5..664c6c47b1 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -564,6 +564,7 @@ AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ if ENABLE_APPARMOR AM_CFLAGS += -DHAVE_APPARMOR +AM_CFLAGS += -DAPPARMOR_CACHE_DIR=\"$(APPARMOR_CACHE_DIR)\" endif if ENABLE_SECCOMP From lxc-bot at linuxcontainers.org Tue Oct 27 15:22:08 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Tue, 27 Oct 2020 08:22:08 -0700 (PDT) Subject: [lxc-devel] [lxc-ci/master] Add Fedora 33 Message-ID: <5f983b20.1c69fb81.7ec15.256cSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 303 bytes Desc: not available URL: -------------- next part -------------- From 02ca12378ef8890337515f819fad64713cc682a4 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Tue, 27 Oct 2020 16:08:02 +0100 Subject: [PATCH 1/2] jenkins/jobs: Add Fedora 33 Signed-off-by: Thomas Hipp --- jenkins/jobs/image-fedora.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/jenkins/jobs/image-fedora.yaml b/jenkins/jobs/image-fedora.yaml index 953032b..5d585f1 100644 --- a/jenkins/jobs/image-fedora.yaml +++ b/jenkins/jobs/image-fedora.yaml @@ -22,6 +22,7 @@ values: - 31 - 32 + - 33 - axis: name: variant From 1e4f57dafc470fdc37fe762c9815bb5447daef8c Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Tue, 27 Oct 2020 16:08:48 +0100 Subject: [PATCH 2/2] images: Remove obsolete filters in Fedora Signed-off-by: Thomas Hipp --- images/fedora.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/images/fedora.yaml b/images/fedora.yaml index 7cd808e..f4261bb 100644 --- a/images/fedora.yaml +++ b/images/fedora.yaml @@ -114,9 +114,6 @@ files: BindReadOnlyPaths=/sys variants: - default - releases: - - 31 - - 32 types: - container From lxc-bot at linuxcontainers.org Tue Oct 27 16:00:57 2020 From: lxc-bot at linuxcontainers.org (blenk92 on Github) Date: Tue, 27 Oct 2020 09:00:57 -0700 (PDT) Subject: [lxc-devel] [lxc/master] lxc-attach: Enable setting the SELinux context Message-ID: <5f984439.1c69fb81.5b902.4d85SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 529 bytes Desc: not available URL: -------------- next part -------------- From d5cfc7dc486d3a3dcf3d56d78d1edef8efa39b00 Mon Sep 17 00:00:00 2001 From: Maximilian Blenk Date: Tue, 27 Oct 2020 10:38:44 +0100 Subject: [PATCH] lxc-attach: Enable setting the SELinux context Enable lxc-attach to set the SELinux context that the user will end up in when attaching to a container (This can be used to overwrite the context set in the config file). If the option is not used, behavior will be as before --- src/lxc/attach.c | 5 +++-- src/lxc/attach_options.h | 3 +++ src/lxc/tools/lxc_attach.c | 10 ++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/lxc/attach.c b/src/lxc/attach.c index 9528d54064..13224805c3 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -657,6 +657,7 @@ static int attach_child_main(struct attach_clone_payload *payload) bool needs_lsm = (options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label; + char *lsm_label = NULL; /* A description of the purpose of this functionality is provided in the * lxc-attach(1) manual page. We have to remount here and not in the @@ -778,9 +779,9 @@ static int attach_child_main(struct attach_clone_payload *payload) /* Change into our new LSM profile. */ on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false; - + lsm_label = options->lsm_label ? options->lsm_label : init_ctx->lsm_label; ret = init_ctx->lsm_ops->process_label_set_at(init_ctx->lsm_ops, lsm_fd, - init_ctx->lsm_label, on_exec); + lsm_label, on_exec); close(lsm_fd); if (ret < 0) goto on_error; diff --git a/src/lxc/attach_options.h b/src/lxc/attach_options.h index 63e62d4ff0..cdcd8f8ece 100644 --- a/src/lxc/attach_options.h +++ b/src/lxc/attach_options.h @@ -113,6 +113,9 @@ typedef struct lxc_attach_options_t { /*! File descriptor to log output. */ int log_fd; + + /*! lsm label to set. */ + char *lsm_label; } lxc_attach_options_t; /*! Default attach options to use */ diff --git a/src/lxc/tools/lxc_attach.c b/src/lxc/tools/lxc_attach.c index a8f493aa71..7c70eae51e 100644 --- a/src/lxc/tools/lxc_attach.c +++ b/src/lxc/tools/lxc_attach.c @@ -59,6 +59,7 @@ static char **extra_env; static ssize_t extra_env_size; static char **extra_keep; static ssize_t extra_keep_size; +static char *selinux_context = NULL; static const struct option my_longopts[] = { {"elevated-privileges", optional_argument, 0, 'e'}, @@ -74,6 +75,7 @@ static const struct option my_longopts[] = { {"rcfile", required_argument, 0, 'f'}, {"uid", required_argument, 0, 'u'}, {"gid", required_argument, 0, 'g'}, + {"context", required_argument, 0, 'c'}, LXC_COMMON_OPTIONS }; @@ -126,6 +128,8 @@ Options :\n\ Load configuration file FILE\n\ -u, --uid=UID Execute COMMAND with UID inside the container\n\ -g, --gid=GID Execute COMMAND with GID inside the container\n\ + -c, --context=context\n\ + SELinux Context to transition into\n\ ", .options = my_longopts, .parser = my_parser, @@ -201,6 +205,9 @@ static int my_parser(struct lxc_arguments *args, int c, char *arg) if (lxc_safe_uint(arg, &args->gid) < 0) return -1; break; + case 'c': + selinux_context = arg; + break; } return 0; @@ -353,6 +360,9 @@ int main(int argc, char *argv[]) if (my_args.gid != LXC_INVALID_GID) attach_options.gid = my_args.gid; + // selinux_context will be NULL if not set + attach_options.lsm_label = selinux_context; + if (command.program) { ret = c->attach_run_wait(c, &attach_options, command.program, (const char **)command.argv); From noreply at github.com Tue Oct 27 16:13:22 2020 From: noreply at github.com (Christian Brauner) Date: Tue, 27 Oct 2020 09:13:22 -0700 Subject: [lxc-devel] [lxc/lxc] 6eb516: lxccontainer: fix lxc_config_item_is_supported Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: 6eb516a793edd7c8e37472d00d1fc599f176bb97 https://github.com/lxc/lxc/commit/6eb516a793edd7c8e37472d00d1fc599f176bb97 Author: Ruben Jenster Date: 2020-10-27 (Tue, 27 Oct 2020) Changed paths: M src/lxc/confile.c M src/lxc/confile.h M src/lxc/lxccontainer.c M src/tests/get_item.c Log Message: ----------- lxccontainer: fix lxc_config_item_is_supported Use exact match instead of longest prefix match to check whether a config item is supported. Signed-off-by: Ruben Jenster Commit: dd8d55091991d7cbbef20b93a06fd500d0d8e4d2 https://github.com/lxc/lxc/commit/dd8d55091991d7cbbef20b93a06fd500d0d8e4d2 Author: Christian Brauner Date: 2020-10-27 (Tue, 27 Oct 2020) Changed paths: M src/lxc/confile.c M src/lxc/confile.h M src/lxc/lxccontainer.c M src/tests/get_item.c Log Message: ----------- Merge pull request #3564 from Drachenfels-GmbH/fixes lxccontainer: fix lxc_config_item_is_supported Compare: https://github.com/lxc/lxc/compare/c8fe11552a41...dd8d55091991 From noreply at github.com Tue Oct 27 16:14:38 2020 From: noreply at github.com (Christian Brauner) Date: Tue, 27 Oct 2020 09:14:38 -0700 Subject: [lxc-devel] [lxc/lxc] beff99: tests: Fix compilation with appamor enabled. Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: beff99393906f3bddff186993203a8b9c0ba9dfc https://github.com/lxc/lxc/commit/beff99393906f3bddff186993203a8b9c0ba9dfc Author: Ruben Jenster Date: 2020-10-27 (Tue, 27 Oct 2020) Changed paths: M src/tests/Makefile.am Log Message: ----------- tests: Fix compilation with appamor enabled. Signed-off-by: Ruben Jenster Commit: 10397a8031bde1b927b9b9c16918907b04349e02 https://github.com/lxc/lxc/commit/10397a8031bde1b927b9b9c16918907b04349e02 Author: Christian Brauner Date: 2020-10-27 (Tue, 27 Oct 2020) Changed paths: M src/tests/Makefile.am Log Message: ----------- Merge pull request #3565 from Drachenfels-GmbH/test-fixes tests: Fix compilation with appamor enabled. Compare: https://github.com/lxc/lxc/compare/dd8d55091991...10397a8031bd From noreply at github.com Tue Oct 27 16:44:58 2020 From: noreply at github.com (Christian Brauner) Date: Tue, 27 Oct 2020 09:44:58 -0700 Subject: [lxc-devel] [lxc/lxc] 0ff0d2: seccomp: Fix handling of pseudo syscalls and impro... Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: 0ff0d23e4001ec9cadae51b41e834a954ef5026c https://github.com/lxc/lxc/commit/0ff0d23e4001ec9cadae51b41e834a954ef5026c Author: Ruben Jenster Date: 2020-10-27 (Tue, 27 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- seccomp: Fix handling of pseudo syscalls and improve logging for rule processing. Signed-off-by: Ruben Jenster Commit: 15044cd19c8454b20ee46fdb17dd0c8dd85366b1 https://github.com/lxc/lxc/commit/15044cd19c8454b20ee46fdb17dd0c8dd85366b1 Author: Ruben Jenster Date: 2020-10-27 (Tue, 27 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- seccomp: Avoid duplicate processing of rules for host native arch. Signed-off-by: Ruben Jenster Commit: 5fd31e375f52a9debb7de5c2112e3df9fd509482 https://github.com/lxc/lxc/commit/5fd31e375f52a9debb7de5c2112e3df9fd509482 Author: Christian Brauner Date: 2020-10-27 (Tue, 27 Oct 2020) Changed paths: M src/lxc/seccomp.c Log Message: ----------- Merge pull request #3562 from Drachenfels-GmbH/seccomp-fixes seccomp: fix pseudo syscalls, improve logging and avoid duplicate processing Compare: https://github.com/lxc/lxc/compare/10397a8031bd...5fd31e375f52 From noreply at github.com Tue Oct 27 16:45:18 2020 From: noreply at github.com (Christian Brauner) Date: Tue, 27 Oct 2020 09:45:18 -0700 Subject: [lxc-devel] [lxc/lxc] 7696c1: Introduce lxc.cgroup.dir.monitor.pivot Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: 7696c1f9d1aed98a54bf7acd4c48799c395cdc64 https://github.com/lxc/lxc/commit/7696c1f9d1aed98a54bf7acd4c48799c395cdc64 Author: Ruben Jenster Date: 2020-10-27 (Tue, 27 Oct 2020) Changed paths: M doc/lxc.container.conf.sgml.in M src/lxc/cgroups/cgfsng.c M src/lxc/conf.h M src/lxc/confile.c Log Message: ----------- Introduce lxc.cgroup.dir.monitor.pivot On termination lxc may fail to remove either lxc.cgroup.dir or lxc.cgroup.dir.monitor, because the monitor process may still be a member of either of these cgroups. The pivot cgroup should not be a member (subpath) of any other container cgroup (dir). because only empty cgroups can be removed. Signed-off-by: Ruben Jenster Commit: a093bb0f5c3d61d3d098ddeea9722a135b95a36e https://github.com/lxc/lxc/commit/a093bb0f5c3d61d3d098ddeea9722a135b95a36e Author: Christian Brauner Date: 2020-10-27 (Tue, 27 Oct 2020) Changed paths: M doc/lxc.container.conf.sgml.in M src/lxc/cgroups/cgfsng.c M src/lxc/conf.h M src/lxc/confile.c Log Message: ----------- Merge pull request #3563 from Drachenfels-GmbH/cgroup-fixes cgroups: Introduce lxc.cgroup.dir.monitor.pivot - fixes cgroup removal on termination Compare: https://github.com/lxc/lxc/compare/5fd31e375f52...a093bb0f5c3d From noreply at github.com Tue Oct 27 16:46:04 2020 From: noreply at github.com (Christian Brauner) Date: Tue, 27 Oct 2020 09:46:04 -0700 Subject: [lxc-devel] [lxc/lxc] 8455e3: lxc-attach: Enable setting the SELinux context Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: 8455e39efe383d520ac3936535630079bebdd2ad https://github.com/lxc/lxc/commit/8455e39efe383d520ac3936535630079bebdd2ad Author: Maximilian Blenk Date: 2020-10-27 (Tue, 27 Oct 2020) Changed paths: M src/lxc/attach.c M src/lxc/attach_options.h M src/lxc/tools/lxc_attach.c Log Message: ----------- lxc-attach: Enable setting the SELinux context Enable lxc-attach to set the SELinux context that the user will end up in when attaching to a container (This can be used to overwrite the context set in the config file). If the option is not used, behavior will be as before Signed-off-by: Maximilian Blenk Commit: bf0b9c1ed6a9ba0ad7103cbc2283f4737bfd99f7 https://github.com/lxc/lxc/commit/bf0b9c1ed6a9ba0ad7103cbc2283f4737bfd99f7 Author: Christian Brauner Date: 2020-10-27 (Tue, 27 Oct 2020) Changed paths: M src/lxc/attach.c M src/lxc/attach_options.h M src/lxc/tools/lxc_attach.c Log Message: ----------- Merge pull request #3567 from blenk92/lxc-attach-selinux lxc-attach: Enable setting the SELinux context Compare: https://github.com/lxc/lxc/compare/a093bb0f5c3d...bf0b9c1ed6a9 From lxc-bot at linuxcontainers.org Tue Oct 27 17:24:55 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Tue, 27 Oct 2020 10:24:55 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Instance: Write out updated backup.yaml after rename Message-ID: <5f9857e7.1c69fb81.18f21.a802SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 376 bytes Desc: not available URL: -------------- next part -------------- From 74c1e881df63a4d211e674e790459925a321ac5e Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Tue, 27 Oct 2020 17:24:08 +0000 Subject: [PATCH] lxd/instance/drivers: Write out updated backup.yaml after rename Fixes #8071 Signed-off-by: Thomas Parrott --- lxd/instance/drivers/driver_lxc.go | 5 +++++ lxd/instance/drivers/driver_qemu.go | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go index 0efab52699..a89a68e866 100644 --- a/lxd/instance/drivers/driver_lxc.go +++ b/lxd/instance/drivers/driver_lxc.go @@ -3747,6 +3747,11 @@ func (c *lxc) Rename(newName string) error { // Update lease files. network.UpdateDNSMasqStatic(c.state, "") + err = c.UpdateBackupFile() + if err != nil { + return err + } + logger.Info("Renamed container", ctxMap) if c.IsSnapshot() { diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index ba1381043a..d5e81b5200 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -2751,6 +2751,11 @@ func (vm *qemu) Rename(newName string) error { // Update lease files. network.UpdateDNSMasqStatic(vm.state, "") + err = vm.UpdateBackupFile() + if err != nil { + return err + } + logger.Info("Renamed instance", ctxMap) if vm.IsSnapshot() { From lxc-bot at linuxcontainers.org Tue Oct 27 20:38:13 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Tue, 27 Oct 2020 13:38:13 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd: Switch to new candid URL Message-ID: <5f988535.1c69fb81.eed7c.b2d3SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 370 bytes Desc: not available URL: -------------- next part -------------- From 7bad0cbaecdefef16e2c636a5d1a77aa49e54924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 27 Oct 2020 16:37:45 -0400 Subject: [PATCH] lxd: Switch to new candid URL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #8085 Signed-off-by: Stéphane Graber --- lxd/daemon.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/daemon.go b/lxd/daemon.go index e04cafc397..52308da8c3 100644 --- a/lxd/daemon.go +++ b/lxd/daemon.go @@ -16,7 +16,7 @@ import ( "sync" "time" - "github.com/CanonicalLtd/candidclient" + "github.com/canonical/candid/candidclient" dqliteclient "github.com/canonical/go-dqlite/client" "github.com/canonical/go-dqlite/driver" "github.com/gorilla/mux" From lxc-bot at linuxcontainers.org Tue Oct 27 22:06:44 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Tue, 27 Oct 2020 15:06:44 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/storage/zfs: No need to remove dashes from UUID Message-ID: <5f9899f4.1c69fb81.de82.e96aSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From 7b5aae3986c7e364e62e12064c73ba439503ddd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 27 Oct 2020 17:35:49 -0400 Subject: [PATCH] lxd/storage/zfs: No need to remove dashes from UUID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/storage/drivers/driver_zfs_volumes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go index 1895fe6a4b..811fb11450 100644 --- a/lxd/storage/drivers/driver_zfs_volumes.go +++ b/lxd/storage/drivers/driver_zfs_volumes.go @@ -80,7 +80,7 @@ func (d *zfs) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Oper // be restored in the future and a new cached image volume will be created instead. if volSizeBytes > poolVolSizeBytes { d.logger.Debug("Renaming deleted cached image volume so that regeneration is used") - randomVol := NewVolume(d, d.name, vol.volType, vol.contentType, strings.Replace(uuid.NewRandom().String(), "-", "", -1), vol.config, vol.poolConfig) + randomVol := NewVolume(d, d.name, vol.volType, vol.contentType, uuid.NewRandom().String(), vol.config, vol.poolConfig) _, err := shared.RunCommand("/proc/self/exe", "forkzfs", "--", "rename", d.dataset(vol, true), d.dataset(randomVol, true)) if err != nil { From lxc-bot at linuxcontainers.org Tue Oct 27 23:11:21 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Tue, 27 Oct 2020 16:11:21 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Log username on unix queries Message-ID: <5f98a919.1c69fb81.13be3.456fSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From f3c4805b2267aa6e8af890499ee8c72673522b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 27 Oct 2020 18:56:45 -0400 Subject: [PATCH 1/5] shared: Drop GroupId and UserId MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- shared/util_linux_cgo.go | 97 ---------------------------------------- 1 file changed, 97 deletions(-) diff --git a/shared/util_linux_cgo.go b/shared/util_linux_cgo.go index 72d0794953..155975d265 100644 --- a/shared/util_linux_cgo.go +++ b/shared/util_linux_cgo.go @@ -6,7 +6,6 @@ package shared import ( "fmt" "os" - "unsafe" // Used by cgo _ "github.com/lxc/lxd/lxd/include" @@ -58,102 +57,6 @@ import "C" const ABSTRACT_UNIX_SOCK_LEN int = C.ABSTRACT_UNIX_SOCK_LEN -// UserId is an adaption from https://codereview.appspot.com/4589049. -func UserId(name string) (int, error) { - var pw C.struct_passwd - var result *C.struct_passwd - - bufSize := C.sysconf(C._SC_GETPW_R_SIZE_MAX) - if bufSize < 0 { - bufSize = 4096 - } - - buf := C.malloc(C.size_t(bufSize)) - if buf == nil { - return -1, fmt.Errorf("allocation failed") - } - defer C.free(buf) - - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - -again: - rv, errno := C.getpwnam_r(cname, - &pw, - (*C.char)(buf), - C.size_t(bufSize), - &result) - if rv < 0 { - // OOM killer will take care of us if we end up doing this too - // often. - if errno == unix.ERANGE { - bufSize *= 2 - tmp := C.realloc(buf, C.size_t(bufSize)) - if tmp == nil { - return -1, fmt.Errorf("allocation failed") - } - buf = tmp - goto again - } - return -1, fmt.Errorf("failed user lookup: %s", unix.Errno(rv)) - } - - if result == nil { - return -1, fmt.Errorf("unknown user %s", name) - } - - return int(C.int(result.pw_uid)), nil -} - -// GroupId is an adaption from https://codereview.appspot.com/4589049. -func GroupId(name string) (int, error) { - var grp C.struct_group - var result *C.struct_group - - bufSize := C.sysconf(C._SC_GETGR_R_SIZE_MAX) - if bufSize < 0 { - bufSize = 4096 - } - - buf := C.malloc(C.size_t(bufSize)) - if buf == nil { - return -1, fmt.Errorf("allocation failed") - } - - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - -again: - rv, errno := C.getgrnam_r(cname, - &grp, - (*C.char)(buf), - C.size_t(bufSize), - &result) - if rv != 0 { - // OOM killer will take care of us if we end up doing this too - // often. - if errno == unix.ERANGE { - bufSize *= 2 - tmp := C.realloc(buf, C.size_t(bufSize)) - if tmp == nil { - return -1, fmt.Errorf("allocation failed") - } - buf = tmp - goto again - } - - C.free(buf) - return -1, fmt.Errorf("failed group lookup: %s", unix.Errno(rv)) - } - C.free(buf) - - if result == nil { - return -1, fmt.Errorf("unknown group %s", name) - } - - return int(C.int(result.gr_gid)), nil -} - func ReadPid(r *os.File) int { return int(C.read_pid(C.int(r.Fd()))) } From 30e814b42b0a19355c2bad3a2e971c7bf44c5f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 27 Oct 2020 18:57:23 -0400 Subject: [PATCH 2/5] lxd: Port to os/user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/endpoints/socket.go | 14 ++++++++++---- lxd/sys/os.go | 24 ++++++++++++++++++------ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lxd/endpoints/socket.go b/lxd/endpoints/socket.go index 6eeccba3fc..98a81a2646 100644 --- a/lxd/endpoints/socket.go +++ b/lxd/endpoints/socket.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "os" + "os/user" "strconv" "github.com/lxc/lxd/client" @@ -86,14 +87,19 @@ func socketUnixSetPermissions(path string, mode os.FileMode) error { } // Change the ownership of the given unix socket file, -func socketUnixSetOwnership(path string, group string) error { +func socketUnixSetOwnership(path string, groupName string) error { var gid int var err error - if group != "" { - gid, err = shared.GroupId(group) + if groupName != "" { + g, err := user.LookupGroup(groupName) if err != nil { - return fmt.Errorf("cannot get group ID of '%s': %v", group, err) + return fmt.Errorf("cannot get group ID of '%s': %v", groupName, err) + } + + gid, err = strconv.Atoi(g.Gid) + if err != nil { + return err } } else { gid = os.Getgid() diff --git a/lxd/sys/os.go b/lxd/sys/os.go index 61cdf47394..0ab6d8f281 100644 --- a/lxd/sys/os.go +++ b/lxd/sys/os.go @@ -3,7 +3,9 @@ package sys import ( + "os/user" "path/filepath" + "strconv" "sync" log "github.com/lxc/lxd/shared/log15" @@ -115,24 +117,34 @@ func (s *OS) Init() error { } // Detect if it is possible to run daemons as an unprivileged user and group. - for _, user := range []string{"lxd", "nobody"} { - uid, err := shared.UserId(user) + for _, userName := range []string{"lxd", "nobody"} { + u, err := user.Lookup(userName) if err != nil { continue } - s.UnprivUser = user + uid, err := strconv.ParseUint(u.Uid, 10, 32) + if err != nil { + return err + } + + s.UnprivUser = userName s.UnprivUID = uint32(uid) break } - for _, group := range []string{"lxd", "nogroup"} { - gid, err := shared.GroupId(group) + for _, groupName := range []string{"lxd", "nogroup"} { + g, err := user.LookupGroup(groupName) if err != nil { continue } - s.UnprivGroup = group + gid, err := strconv.ParseUint(g.Gid, 10, 32) + if err != nil { + return err + } + + s.UnprivGroup = groupName s.UnprivGID = uint32(gid) break } From a38f4092d40c68c77a704353776bf7edce98edd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 27 Oct 2020 19:07:58 -0400 Subject: [PATCH 3/5] lxd/daemon: Log protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/daemon.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/daemon.go b/lxd/daemon.go index 52308da8c3..1de4938c70 100644 --- a/lxd/daemon.go +++ b/lxd/daemon.go @@ -425,7 +425,7 @@ func (d *Daemon) createCmd(restAPI *mux.Router, version string, c APIEndpoint) { untrustedOk := (r.Method == "GET" && c.Get.AllowUntrusted) || (r.Method == "POST" && c.Post.AllowUntrusted) if trusted { - logger.Debug("Handling", log.Ctx{"method": r.Method, "url": r.URL.RequestURI(), "ip": r.RemoteAddr, "user": username}) + logger.Debug("Handling", log.Ctx{"method": r.Method, "url": r.URL.RequestURI(), "ip": r.RemoteAddr, "username": username, "protocol": protocol}) r = r.WithContext(context.WithValue(context.WithValue(r.Context(), "username", username), "protocol", protocol)) } else if untrustedOk && r.Header.Get("X-LXD-authenticated") == "" { logger.Debug(fmt.Sprintf("Allowing untrusted %s", r.Method), log.Ctx{"url": r.URL.RequestURI(), "ip": r.RemoteAddr}) From 1694cfd678f4eadeddda4831f3103a1d3aaed439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 27 Oct 2020 19:08:35 -0400 Subject: [PATCH 4/5] lxd/daemon: Pass writer to Authenticate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/certificates.go | 2 +- lxd/daemon.go | 6 +++--- lxd/images.go | 2 +- lxd/operations.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lxd/certificates.go b/lxd/certificates.go index 917f5d1178..673ae5a386 100644 --- a/lxd/certificates.go +++ b/lxd/certificates.go @@ -125,7 +125,7 @@ func certificatesPost(d *Daemon, r *http.Request) response.Response { return response.SmartError(err) } - trusted, _, protocol, err := d.Authenticate(r) + trusted, _, protocol, err := d.Authenticate(nil, r) if err != nil { return response.SmartError(err) } diff --git a/lxd/daemon.go b/lxd/daemon.go index 1de4938c70..94f6728b1c 100644 --- a/lxd/daemon.go +++ b/lxd/daemon.go @@ -241,7 +241,7 @@ func allowProjectPermission(feature string, permission string) func(d *Daemon, r // Convenience function around Authenticate func (d *Daemon) checkTrustedClient(r *http.Request) error { - trusted, _, _, err := d.Authenticate(r) + trusted, _, _, err := d.Authenticate(nil, r) if !trusted || err != nil { if err != nil { return err @@ -258,7 +258,7 @@ func (d *Daemon) checkTrustedClient(r *http.Request) error { // will validate the TLS certificate or Macaroon. // // This does not perform authorization, only validates authentication -func (d *Daemon) Authenticate(r *http.Request) (bool, string, string, error) { +func (d *Daemon) Authenticate(w http.ResponseWriter, r *http.Request) (bool, string, string, error) { // Allow internal cluster traffic if r.TLS != nil { cert, _ := x509.ParseCertificate(d.endpoints.NetworkCert().KeyPair().Certificate[0]) @@ -403,7 +403,7 @@ func (d *Daemon) createCmd(restAPI *mux.Router, version string, c APIEndpoint) { } // Authentication - trusted, username, protocol, err := d.Authenticate(r) + trusted, username, protocol, err := d.Authenticate(w, r) if err != nil { // If not a macaroon discharge request, return the error _, ok := err.(*bakery.DischargeRequiredError) diff --git a/lxd/images.go b/lxd/images.go index d97e759408..4fb55b9087 100644 --- a/lxd/images.go +++ b/lxd/images.go @@ -696,7 +696,7 @@ func imageCreateInPool(d *Daemon, info *api.Image, storagePool string) error { } func imagesPost(d *Daemon, r *http.Request) response.Response { - trusted, _, _, _ := d.Authenticate(r) + trusted, _, _, _ := d.Authenticate(nil, r) secret := r.Header.Get("X-LXD-secret") fingerprint := r.Header.Get("X-LXD-fingerprint") diff --git a/lxd/operations.go b/lxd/operations.go index e5d934c9ed..b46427f1cf 100644 --- a/lxd/operations.go +++ b/lxd/operations.go @@ -372,7 +372,7 @@ func operationWaitGet(d *Daemon, r *http.Request) response.Response { id := mux.Vars(r)["id"] secret := r.FormValue("secret") - trusted, _, _, _ := d.Authenticate(r) + trusted, _, _, _ := d.Authenticate(nil, r) if !trusted && secret == "" { return response.Forbidden(nil) } From e5a01555ba6d1e25ce859be84a5b9b9d4a88b8ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 27 Oct 2020 19:08:53 -0400 Subject: [PATCH 5/5] lxd/daemon: Record username on unix queries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #8012 Signed-off-by: Stéphane Graber --- lxd/daemon.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lxd/daemon.go b/lxd/daemon.go index 94f6728b1c..8e8cd12c20 100644 --- a/lxd/daemon.go +++ b/lxd/daemon.go @@ -11,6 +11,7 @@ import ( "net/http" "net/url" "os" + "os/user" "path/filepath" "strings" "sync" @@ -37,6 +38,7 @@ import ( "github.com/lxc/lxd/lxd/events" "github.com/lxc/lxd/lxd/firewall" "github.com/lxc/lxd/lxd/instance" + "github.com/lxc/lxd/lxd/ucred" // Import instance/drivers without name so init() runs. _ "github.com/lxc/lxd/lxd/instance/drivers" @@ -273,6 +275,21 @@ func (d *Daemon) Authenticate(w http.ResponseWriter, r *http.Request) (bool, str // Local unix socket queries if r.RemoteAddr == "@" { + if w != nil { + conn := extractUnderlyingConn(w) + cred, err := ucred.GetCred(conn) + if err != nil { + return false, "", "", err + } + + u, err := user.LookupId(fmt.Sprintf("%d", cred.Uid)) + if err != nil { + return true, fmt.Sprintf("uid=%d", cred.Uid), "unix", nil + } + + return true, u.Username, "unix", nil + } + return true, "", "unix", nil } From lxc-bot at linuxcontainers.org Tue Oct 27 23:21:25 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Tue, 27 Oct 2020 16:21:25 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/storage: Lock during the whole image replace Message-ID: <5f98ab75.1c69fb81.2c730.23c1SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From 6b4533a92cfe6eacabe6a1393270b7d7949d2b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 27 Oct 2020 18:23:03 -0400 Subject: [PATCH] lxd/storage: Lock during the whole image replace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/storage/backend_lxd.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index b2c1272991..f3d8f83273 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -1039,6 +1039,11 @@ func (b *lxdBackend) CreateInstanceFromImage(inst instance.Instance, fingerprint // by creating a new cached image volume using the pool's current settings (including volume.size). if errors.Cause(err) == drivers.ErrCannotBeShrunk { logger.Debug("Cached image volume is larger than new volume and cannot be shrunk, regenerating image volume") + + // Lock during the entire process to avoid attempts at creating while the image is gone. + unlock := locking.Lock(drivers.OperationLockName(b.name, string(drivers.VolumeTypeImage), fmt.Sprintf("ReplaceImage_%v", fingerprint))) + defer unlock() + err = b.DeleteImage(fingerprint, op) if err != nil { return err @@ -1055,7 +1060,6 @@ func (b *lxdBackend) CreateInstanceFromImage(inst instance.Instance, fingerprint } } else if err != nil { return err - } } From builds at travis-ci.org Wed Oct 28 01:40:46 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 28 Oct 2020 01:40:46 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7928 (master - bf0b9c1) In-Reply-To: Message-ID: <5f98cc1e53b56_13fcb40cae4f083939@travis-tasks-cb988dd6-nsrpr.mail> Build Update for lxc/lxc ------------------------------------- Build: #7928 Status: Errored Duration: 6 hrs, 52 mins, and 13 secs Commit: bf0b9c1 (master) Author: Christian Brauner Message: Merge pull request #3567 from blenk92/lxc-attach-selinux lxc-attach: Enable setting the SELinux context View the changeset: https://github.com/lxc/lxc/compare/a093bb0f5c3d...bf0b9c1ed6a9 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/739353532?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lxc-bot at linuxcontainers.org Wed Oct 28 03:07:16 2020 From: lxc-bot at linuxcontainers.org (brauner on Github) Date: Tue, 27 Oct 2020 20:07:16 -0700 (PDT) Subject: [lxc-devel] [lxc/master] coverity fixes Message-ID: <5f98e064.1c69fb81.dd55f.e6fcSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 364 bytes Desc: not available URL: -------------- next part -------------- From ec0befee9475aa7d6913ee0da24761d66b111797 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 28 Oct 2020 03:58:54 +0100 Subject: [PATCH 1/4] commands: don't deref after NULL check Fixes: Coverity 1465657 Signed-off-by: Christian Brauner --- src/lxc/commands.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lxc/commands.c b/src/lxc/commands.c index de09c3aff0..cca09a1261 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -487,9 +487,12 @@ static int lxc_cmd_get_devpts_fd_callback(int fd, struct lxc_cmd_req *req, }; int ret; - if (!handler->conf || handler->conf->devpts_fd < 0) + if (!handler->conf || handler->conf->devpts_fd < 0) { rsp.ret = -EBADF; - ret = lxc_abstract_unix_send_fds(fd, &handler->conf->devpts_fd, 1, &rsp, sizeof(rsp)); + ret = lxc_abstract_unix_send_fds(fd, NULL, 0, &rsp, sizeof(rsp)); + } else { + ret = lxc_abstract_unix_send_fds(fd, &handler->conf->devpts_fd, 1, &rsp, sizeof(rsp)); + } if (ret < 0) return log_error(LXC_CMD_REAP_CLIENT_FD, "Failed to send devpts fd"); From 3715d0c03fae815963cbcef66524a2deffda39e0 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 28 Oct 2020 04:01:19 +0100 Subject: [PATCH 2/4] utils: don't deref after NULL check Fixes: Coverity 1465855 Signed-off-by: Christian Brauner --- src/lxc/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/utils.c b/src/lxc/utils.c index c02eef1526..561f7685cc 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -1113,7 +1113,7 @@ int safe_mount_beneath(const char *beneath, const char *src, const char *dst, co __do_close int beneath_fd = -EBADF; const char *path = beneath ? beneath : "/"; - beneath_fd = openat(-1, beneath, O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_PATH); + beneath_fd = openat(-1, path, O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_PATH); if (beneath_fd < 0) return log_error_errno(-errno, errno, "Failed to open %s", path); From 8ddf34f7a037325565b8cf8ff995cbf573f9932e Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 28 Oct 2020 04:03:31 +0100 Subject: [PATCH 3/4] conf: check snprint return value Fixes: Coverity 1465854 Signed-off-by: Christian Brauner --- src/lxc/conf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 259d3766ab..c258d0b4c5 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1207,7 +1207,9 @@ static int lxc_fill_autodev(const struct lxc_rootfs *rootfs) if (ret < 0) { const char *mntpt = rootfs->path ? rootfs->mount : NULL; if (errno == ENOSYS) { - snprintf(path, sizeof(path), "%s/dev/%s", mntpt, device->name); + ret = snprintf(path, sizeof(path), "%s/dev/%s", mntpt, device->name); + if (ret < 0 || ret >= sizeof(path)) + return log_error(-1, "Failed to create device path for %s", device->name); ret = safe_mount(hostpath, path, 0, MS_BIND, NULL, rootfs->path ? rootfs->mount : NULL); } } From 0dde733e5a049e695885d733eb98795b0eddbd74 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 28 Oct 2020 04:04:42 +0100 Subject: [PATCH 4/4] utils: check snprintf return value Fixes: Coverity 1465853 Signed-off-by: Christian Brauner --- src/lxc/utils.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lxc/utils.c b/src/lxc/utils.c index 561f7685cc..baf80b7f5c 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -1097,7 +1097,9 @@ int __safe_mount_beneath_at(int beneath_fd, const char *src, const char *dst, co target_fd = openat2(beneath_fd, dst, &how, sizeof(how)); if (target_fd < 0) return -errno; - snprintf(tgt_buf, sizeof(tgt_buf), "/proc/self/fd/%d", target_fd); + ret = snprintf(tgt_buf, sizeof(tgt_buf), "/proc/self/fd/%d", target_fd); + if (ret < 0 || ret >= sizeof(tgt_buf)) + return -EIO; if (!is_empty_string(src_buf)) ret = mount(src_buf, tgt_buf, fstype, flags, data); From lxc-bot at linuxcontainers.org Wed Oct 28 10:47:53 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Wed, 28 Oct 2020 03:47:53 -0700 (PDT) Subject: [lxc-devel] [lxc-ci/master] bin/test-lxd-ovn: Adds test for passing external IP subnet into network Message-ID: <5f994c59.1c69fb81.5fab1.a464SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 363 bytes Desc: not available URL: -------------- next part -------------- From 65216a680d6c62671460d2e9de418351a76226f3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 28 Oct 2020 10:47:04 +0000 Subject: [PATCH] bin/test-lxd-ovn: Adds test for passing external IP subnet into network Signed-off-by: Thomas Parrott --- bin/test-lxd-ovn | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/bin/test-lxd-ovn b/bin/test-lxd-ovn index fb0480e..a2844cd 100755 --- a/bin/test-lxd-ovn +++ b/bin/test-lxd-ovn @@ -161,6 +161,26 @@ U1_EXT_IPV4="$(lxc list u1 --project testovn -c4 --format=csv | cut -d' ' -f1)" U1_EXT_IPV6="$(lxc list u1 --project testovn -c6 --format=csv | cut -d' ' -f1)" ovn-nbctl --bare --format=csv --column=external_ip,logical_ip,type find nat | grep "${U1_EXT_IPV4},${U1_EXT_IPV4},dnat_and_snat" ovn-nbctl --bare --format=csv --column=external_ip,logical_ip,type find nat | grep "${U1_EXT_IPV6},${U1_EXT_IPV6},dnat_and_snat" +lxc stop -f u1 --project testovn + +# Test external IPs routed to OVN NIC. +lxc network set ovn-virtual-network --project testovn \ + ipv4.address=auto \ + ipv6.address=auto \ + ipv4.nat=true \ + ipv6.nat=true + +! lxc config device set u1 eth0 ipv4.routes.external=198.51.100.0/24 --project testovn || false +! lxc config device set u1 eth0 ipv6.routes.external=2001:db8:1:2::/64 --project testovn || false +lxc config device set u1 eth0 ipv4.routes.external=198.51.100.0/26 --project testovn +lxc config device set u1 eth0 ipv6.routes.external=2001:db8:1:2::/122 --project testovn +lxc start u1 --project testovn +ovn-nbctl --bare --format=csv --column=external_ip,logical_ip,type find nat +ovn-nbctl --bare --format=csv --column=external_ip,logical_ip,type find nat | grep "198.51.100.0,198.51.100.0,dnat_and_snat" +ovn-nbctl --bare --format=csv --column=external_ip,logical_ip,type find nat | grep "198.51.100.63,198.51.100.63,dnat_and_snat" +ovn-nbctl --bare --format=csv --column=external_ip,logical_ip,type find nat | grep "2001:db8:1:2::,2001:db8:1:2::,dnat_and_snat" +ovn-nbctl --bare --format=csv --column=external_ip,logical_ip,type find nat | grep "2001:db8:1:2::3f,2001:db8:1:2::3f,dnat_and_snat" +ovn-nbctl --bare --format=csv --column=external_ip,logical_ip,type find nat | wc -l | grep 132 lxc delete -f u1 --project testovn lxc network delete ovn-virtual-network --project testovn From noreply at github.com Wed Oct 28 12:03:09 2020 From: noreply at github.com (=?UTF-8?B?U3TDqXBoYW5lIEdyYWJlcg==?=) Date: Wed, 28 Oct 2020 05:03:09 -0700 Subject: [lxc-devel] [lxc/lxc] ec0bef: commands: don't deref after NULL check Message-ID: Branch: refs/heads/master Home: https://github.com/lxc/lxc Commit: ec0befee9475aa7d6913ee0da24761d66b111797 https://github.com/lxc/lxc/commit/ec0befee9475aa7d6913ee0da24761d66b111797 Author: Christian Brauner Date: 2020-10-28 (Wed, 28 Oct 2020) Changed paths: M src/lxc/commands.c Log Message: ----------- commands: don't deref after NULL check Fixes: Coverity 1465657 Signed-off-by: Christian Brauner Commit: 3715d0c03fae815963cbcef66524a2deffda39e0 https://github.com/lxc/lxc/commit/3715d0c03fae815963cbcef66524a2deffda39e0 Author: Christian Brauner Date: 2020-10-28 (Wed, 28 Oct 2020) Changed paths: M src/lxc/utils.c Log Message: ----------- utils: don't deref after NULL check Fixes: Coverity 1465855 Signed-off-by: Christian Brauner Commit: 8ddf34f7a037325565b8cf8ff995cbf573f9932e https://github.com/lxc/lxc/commit/8ddf34f7a037325565b8cf8ff995cbf573f9932e Author: Christian Brauner Date: 2020-10-28 (Wed, 28 Oct 2020) Changed paths: M src/lxc/conf.c Log Message: ----------- conf: check snprint return value Fixes: Coverity 1465854 Signed-off-by: Christian Brauner Commit: 0dde733e5a049e695885d733eb98795b0eddbd74 https://github.com/lxc/lxc/commit/0dde733e5a049e695885d733eb98795b0eddbd74 Author: Christian Brauner Date: 2020-10-28 (Wed, 28 Oct 2020) Changed paths: M src/lxc/utils.c Log Message: ----------- utils: check snprintf return value Fixes: Coverity 1465853 Signed-off-by: Christian Brauner Commit: 65129087f4cd6c4d20ec22461423f871b200a645 https://github.com/lxc/lxc/commit/65129087f4cd6c4d20ec22461423f871b200a645 Author: Christian Brauner Date: 2020-10-28 (Wed, 28 Oct 2020) Changed paths: M src/lxc/attach.c M src/lxc/attach_options.h Log Message: ----------- attach: require that LXC_ATTACH_LSM_LABEL is specified to avoid liblxc stumbling over an smaller struct passed in from an older liblxc. In the future we should version by size but this requires a new attach2(). Signed-off-by: Christian Brauner Commit: 7fde74f3759c2c6b5031c260ee5121fa9f31d4da https://github.com/lxc/lxc/commit/7fde74f3759c2c6b5031c260ee5121fa9f31d4da Author: Stéphane Graber Date: 2020-10-28 (Wed, 28 Oct 2020) Changed paths: M src/lxc/attach.c M src/lxc/attach_options.h M src/lxc/commands.c M src/lxc/conf.c M src/lxc/utils.c Log Message: ----------- Merge pull request #3568 from brauner/2020-10-28/fixes coverity fixes Compare: https://github.com/lxc/lxc/compare/bf0b9c1ed6a9...7fde74f3759c From lxc-bot at linuxcontainers.org Wed Oct 28 14:42:15 2020 From: lxc-bot at linuxcontainers.org (dlemel8 on Github) Date: Wed, 28 Oct 2020 07:42:15 -0700 (PDT) Subject: [lxc-devel] [lxd/master] add new restart operation lock to avoid emitting needless stop/start events Message-ID: <5f998347.1c69fb81.ac09b.f399SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 314 bytes Desc: not available URL: -------------- next part -------------- From af146c569dd0d703a709bb06fd9c89dc4b9934de Mon Sep 17 00:00:00 2001 From: Daniel Segal Date: Wed, 28 Oct 2020 16:41:26 +0200 Subject: [PATCH] add new restart operation lock to avoid emitting needless stop/start events Signed-off-by: Daniel Segal --- lxd/instance/drivers/driver_lxc.go | 111 ++++++++++++++++++---------- lxd/instance/drivers/driver_qemu.go | 105 ++++++++++++++++++-------- 2 files changed, 146 insertions(+), 70 deletions(-) diff --git a/lxd/instance/drivers/driver_lxc.go b/lxd/instance/drivers/driver_lxc.go index 29a3db459d..91ae9a08b7 100644 --- a/lxd/instance/drivers/driver_lxc.go +++ b/lxd/instance/drivers/driver_lxc.go @@ -2330,12 +2330,18 @@ func (c *lxc) detachInterfaceRename(netns string, ifName string, hostName string func (c *lxc) Start(stateful bool) error { var ctxMap log.Ctx - // Setup a new operation - op, err := operationlock.Create(c.id, "start", false, false) - if err != nil { - return errors.Wrap(err, "Create container start operation") + // Get current or set a new operation + var err error + op := operationlock.Get(c.id) + if op == nil { + op, err = operationlock.Create(c.id, "start", false, false) + if err != nil { + return errors.Wrap(err, "Create container start operation") + } + defer op.Done(nil) + } else if op.Action() != "restart" { + return fmt.Errorf("Container is already running a %s operation", op.Action()) } - defer op.Done(nil) if !daemon.SharedMountsSetup { return fmt.Errorf("Daemon failed to setup shared mounts base: %v. Does security.nesting need to be turned on?", err) @@ -2471,10 +2477,12 @@ func (c *lxc) Start(stateful bool) error { return err } - logger.Info("Started container", ctxMap) - c.state.Events.SendLifecycle(c.project, "container-started", - fmt.Sprintf("/1.0/containers/%s", c.name), nil) + if op.Action() != "restart" { + c.state.Events.SendLifecycle(c.project, "container-started", + fmt.Sprintf("/1.0/containers/%s", c.name), nil) + } + logger.Info("Started container", ctxMap) return nil } @@ -2591,10 +2599,16 @@ func (c *lxc) Stop(stateful bool) error { return fmt.Errorf("The container is already stopped") } - // Setup a new operation - op, err := operationlock.Create(c.id, "stop", false, true) - if err != nil { - return err + // Get current or set a new operation + var err error + op := operationlock.Get(c.id) + if op == nil { + op, err = operationlock.Create(c.id, "stop", false, true) + if err != nil { + return err + } + } else if op.Action() != "restart" { + return fmt.Errorf("Container is already running a %s operation", op.Action()) } ctxMap = log.Ctx{ @@ -2639,10 +2653,12 @@ func (c *lxc) Stop(stateful bool) error { return err } - err = op.Wait() - if err != nil && c.IsRunning() { - logger.Error("Failed stopping container", ctxMap) - return err + if op.Action() != "restart" { + err = op.Wait() + if err != nil && c.IsRunning() { + logger.Error("Failed stopping container", ctxMap) + return err + } } c.stateful = true @@ -2653,10 +2669,13 @@ func (c *lxc) Stop(stateful bool) error { return err } - op.Done(nil) + if op.Action() != "restart" { + op.Done(nil) + c.state.Events.SendLifecycle(c.project, "container-stopped", + fmt.Sprintf("/1.0/containers/%s", c.name), nil) + } + logger.Info("Stopped container", ctxMap) - c.state.Events.SendLifecycle(c.project, "container-stopped", - fmt.Sprintf("/1.0/containers/%s", c.name), nil) return nil } else if shared.PathExists(c.StatePath()) { os.RemoveAll(c.StatePath()) @@ -2711,16 +2730,18 @@ func (c *lxc) Stop(stateful bool) error { return err } - err = op.Wait() - if err != nil && c.IsRunning() { - logger.Error("Failed stopping container", ctxMap) - return err + if op.Action() != "restart" { + err = op.Wait() + if err != nil && c.IsRunning() { + logger.Error("Failed stopping container", ctxMap) + return err + } + + c.state.Events.SendLifecycle(c.project, "container-stopped", + fmt.Sprintf("/1.0/containers/%s", c.name), nil) } logger.Info("Stopped container", ctxMap) - c.state.Events.SendLifecycle(c.project, "container-stopped", - fmt.Sprintf("/1.0/containers/%s", c.name), nil) - return nil } @@ -2733,10 +2754,16 @@ func (c *lxc) Shutdown(timeout time.Duration) error { return fmt.Errorf("The container is already stopped") } - // Setup a new operation - op, err := operationlock.Create(c.id, "stop", true, true) - if err != nil { - return err + // Get current or set a new operation + var err error + op := operationlock.Get(c.id) + if op == nil { + op, err = operationlock.Create(c.id, "stop", true, true) + if err != nil { + return err + } + } else if op.Action() != "restart" { + return fmt.Errorf("Container is already running a %s operation", op.Action()) } ctxMap = log.Ctx{ @@ -2773,21 +2800,29 @@ func (c *lxc) Shutdown(timeout time.Duration) error { return err } - err = op.Wait() - if err != nil && c.IsRunning() { - logger.Error("Failed shutting down container", ctxMap) - return err + if op.Action() != "restart" { + err = op.Wait() + if err != nil && c.IsRunning() { + logger.Error("Failed shutting down container", ctxMap) + return err + } + + c.state.Events.SendLifecycle(c.project, "container-shutdown", + fmt.Sprintf("/1.0/containers/%s", c.name), nil) } logger.Info("Shut down container", ctxMap) - c.state.Events.SendLifecycle(c.project, "container-shutdown", - fmt.Sprintf("/1.0/containers/%s", c.name), nil) - return nil } // Restart restart the instance. func (c *lxc) Restart(timeout time.Duration) error { + op, err := operationlock.Create(c.id, "restart", false, false) + if err != nil { + return err + } + defer op.Done(nil) + return c.common.Restart(c, timeout) } @@ -2822,7 +2857,7 @@ func (c *lxc) onStop(args map[string]string) error { // Pick up the existing stop operation lock created in Stop() function. op := operationlock.Get(c.id) - if op != nil && op.Action() != "stop" { + if op != nil && op.Action() != "stop" && op.Action() != "restart" { return fmt.Errorf("Container is already running a %s operation", op.Action()) } diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 80d303845b..04b39e4708 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -517,7 +517,7 @@ func (vm *qemu) onStop(target string) error { // Pick up the existing stop operation lock created in Stop() function. op := operationlock.Get(vm.id) - if op != nil && op.Action() != "stop" { + if op != nil && op.Action() != "stop" && op.Action() != "restart" { return fmt.Errorf("Instance is already running a %s operation", op.Action()) } @@ -568,10 +568,17 @@ func (vm *qemu) Shutdown(timeout time.Duration) error { return fmt.Errorf("The instance is already stopped") } - // Setup a new operation - op, err := operationlock.Create(vm.id, "stop", true, true) - if err != nil { - return err + // Get current or set a new operation + var err error + op := operationlock.Get(vm.id) + if op == nil { + op, err = operationlock.Create(vm.id, "stop", true, true) + if err != nil { + return err + } + + } else if op.Action() != "restart" { + return fmt.Errorf("Container is already running a %s operation", op.Action()) } // Connect to the monitor. @@ -585,7 +592,9 @@ func (vm *qemu) Shutdown(timeout time.Duration) error { chDisconnect, err := monitor.Wait() if err != nil { if err == qmp.ErrMonitorDisconnect { - op.Done(nil) + if op.Action() != "restart" { + op.Done(nil) + } return nil } @@ -597,7 +606,9 @@ func (vm *qemu) Shutdown(timeout time.Duration) error { err = monitor.Powerdown() if err != nil { if err == qmp.ErrMonitorDisconnect { - op.Done(nil) + if op.Action() != "restart" { + op.Done(nil) + } return nil } @@ -618,19 +629,28 @@ func (vm *qemu) Shutdown(timeout time.Duration) error { <-chDisconnect // Block until VM is not running if no timeout provided. } - // Wait for onStop. - err = op.Wait() - if err != nil && vm.IsRunning() { - return err + if op.Action() != "restart" { + // Wait for onStop. + err = op.Wait() + if err != nil && vm.IsRunning() { + return err + } + + op.Done(nil) + vm.state.Events.SendLifecycle(vm.project, "virtual-machine-shutdown", fmt.Sprintf("/1.0/virtual-machines/%s", vm.name), nil) } - op.Done(nil) - vm.state.Events.SendLifecycle(vm.project, "virtual-machine-shutdown", fmt.Sprintf("/1.0/virtual-machines/%s", vm.name), nil) return nil } // Restart restart the instance. func (vm *qemu) Restart(timeout time.Duration) error { + op, err := operationlock.Create(vm.id, "restart", false, false) + if err != nil { + return err + } + defer op.Done(nil) + return vm.common.Restart(vm, timeout) } @@ -654,12 +674,17 @@ func (vm *qemu) Start(stateful bool) error { return fmt.Errorf("The instance is already running") } - // Setup a new operation - op, err := operationlock.Create(vm.id, "start", false, false) - if err != nil { - return errors.Wrap(err, "Create instance start operation") + // Get current or set a new operation + op := operationlock.Get(vm.id) + if op == nil { + op, err = operationlock.Create(vm.id, "start", false, false) + if err != nil { + return errors.Wrap(err, "Create instance start operation") + } + defer op.Done(nil) + } else if op.Action() != "restart" { + return fmt.Errorf("Container is already running a %s operation", op.Action()) } - defer op.Done(nil) revert := revert.New() defer revert.Fail() @@ -1021,7 +1046,9 @@ func (vm *qemu) Start(stateful bool) error { } revert.Success() - vm.state.Events.SendLifecycle(vm.project, "virtual-machine-started", fmt.Sprintf("/1.0/virtual-machines/%s", vm.name), nil) + if op.Action() != "restart" { + vm.state.Events.SendLifecycle(vm.project, "virtual-machine-started", fmt.Sprintf("/1.0/virtual-machines/%s", vm.name), nil) + } return nil } @@ -2299,17 +2326,25 @@ func (vm *qemu) Stop(stateful bool) error { return fmt.Errorf("Stateful stop isn't supported for VMs at this time") } - // Setup a new operation. - op, err := operationlock.Create(vm.id, "stop", false, true) - if err != nil { - return err + // Get current or set a new operation + var err error + op := operationlock.Get(vm.id) + if op == nil { + op, err = operationlock.Create(vm.id, "stop", false, true) + if err != nil { + return err + } + } else if op.Action() != "restart" { + return fmt.Errorf("Container is already running a %s operation", op.Action()) } // Connect to the monitor. monitor, err := qmp.Connect(vm.monitorPath(), qemuSerialChardevName, vm.getMonitorEventHandler()) if err != nil { // If we fail to connect, it's most likely because the VM is already off. - op.Done(nil) + if op.Action() != "restart" { + op.Done(nil) + } return nil } @@ -2317,7 +2352,9 @@ func (vm *qemu) Stop(stateful bool) error { chDisconnect, err := monitor.Wait() if err != nil { if err == qmp.ErrMonitorDisconnect { - op.Done(nil) + if op.Action() != "restart" { + op.Done(nil) + } return nil } @@ -2329,7 +2366,9 @@ func (vm *qemu) Stop(stateful bool) error { err = monitor.Quit() if err != nil { if err == qmp.ErrMonitorDisconnect { - op.Done(nil) + if op.Action() != "restart" { + op.Done(nil) + } return nil } @@ -2340,13 +2379,15 @@ func (vm *qemu) Stop(stateful bool) error { // Wait for QEMU to exit (can take a while if pending I/O). <-chDisconnect - // Wait for onStop. - err = op.Wait() - if err != nil && vm.IsRunning() { - return err - } + if op.Action() != "restart" { + // Wait for onStop. + err = op.Wait() + if err != nil && vm.IsRunning() { + return err + } - vm.state.Events.SendLifecycle(vm.project, "virtual-machine-stopped", fmt.Sprintf("/1.0/virtual-machines/%s", vm.name), nil) + vm.state.Events.SendLifecycle(vm.project, "virtual-machine-stopped", fmt.Sprintf("/1.0/virtual-machines/%s", vm.name), nil) + } return nil } From lxc-bot at linuxcontainers.org Wed Oct 28 16:36:47 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Wed, 28 Oct 2020 09:36:47 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Fix seemingly random image ordering Message-ID: <5f999e1f.1c69fb81.f8459.09ceSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- Fix seemingly random image ordering by stgraber · Pull Request #8091 · lxc/lxd · GitHub
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix seemingly random image ordering #8091

Open
wants to merge 2 commits into
base: master
from
Open

Fix seemingly random image ordering #8091

wants to merge 2 commits into from

Conversation

@stgraber
Copy link
Member

@stgraber stgraber commented Oct 28, 2020

No description provided.

stgraber added 2 commits Oct 28, 2020
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked issues

Successfully merging this pull request may close these issues.

None yet

1 participant
You can’t perform that action at this time.
From lxc-bot at linuxcontainers.org Wed Oct 28 17:44:40 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Wed, 28 Oct 2020 10:44:40 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: OVN NIC external route overlap validation Message-ID: <5f99ae08.1c69fb81.fd113.25d0SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 703 bytes Desc: not available URL: -------------- next part -------------- From 4c1e883f9dbb88e9e4850ae9c98e5a23839d3f74 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 28 Oct 2020 14:10:00 +0000 Subject: [PATCH 1/6] lxd/project/project: Updates StorageVolumeProjectFromRecord to not return error (as never populated) Signed-off-by: Thomas Parrott --- lxd/project/project.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lxd/project/project.go b/lxd/project/project.go index d7d40479fa..f1dbf556b5 100644 --- a/lxd/project/project.go +++ b/lxd/project/project.go @@ -79,26 +79,26 @@ func StorageVolumeProject(c *db.Cluster, projectName string, volumeType int) (st return "", errors.Wrapf(err, "Failed to load project %q", projectName) } - return StorageVolumeProjectFromRecord(project, volumeType) + return StorageVolumeProjectFromRecord(project, volumeType), nil } -// StorageVolumeProjectFromRecord returns the project name to use to for the volume based on the requested project. -// For custom volume type, if the project specified has the "features.storage.volumes" flag enabled then the +// StorageVolumeProjectFromRecord returns the project name to use to for the volume based on the supplied project. +// For custom volume type, if the project supplied has the "features.storage.volumes" flag enabled then the // project name is returned, otherwise the default project name is returned. For all other volume types the // supplied project's name is returned. -func StorageVolumeProjectFromRecord(p *api.Project, volumeType int) (string, error) { +func StorageVolumeProjectFromRecord(p *api.Project, volumeType int) string { // Non-custom volumes always use the project specified. if volumeType != db.StoragePoolVolumeTypeCustom { - return p.Name, nil + return p.Name } // Custom volumes only use the project specified if the project has the features.storage.volumes feature // enabled, otherwise the legacy behaviour of using the default project for custom volumes is used. if shared.IsTrue(p.Config["features.storage.volumes"]) { - return p.Name, nil + return p.Name } - return Default, nil + return Default } // NetworkProject returns the project name to use for the network based on the requested project. From 94a12db197d93e724177c6e883aa2c8c4d77d66b Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 28 Oct 2020 14:10:51 +0000 Subject: [PATCH 2/6] lxd/project/project: Adds NetworkProjectFromRecord function And updates NetworkProject to use it. Signed-off-by: Thomas Parrott --- lxd/project/project.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/lxd/project/project.go b/lxd/project/project.go index f1dbf556b5..202f059650 100644 --- a/lxd/project/project.go +++ b/lxd/project/project.go @@ -106,16 +106,29 @@ func StorageVolumeProjectFromRecord(p *api.Project, volumeType int) string { // otherwise the default project name is returned. The second return value is the project's config if non-default // project is being returned, nil if not. func NetworkProject(c *db.Cluster, projectName string) (string, map[string]string, error) { - project, err := c.GetProject(projectName) + p, err := c.GetProject(projectName) if err != nil { return "", nil, errors.Wrapf(err, "Failed to load project %q", projectName) } + projectName = NetworkProjectFromRecord(p) + + if projectName != Default { + return projectName, p.Config, nil + } + + return Default, nil, nil +} + +// NetworkProjectFromRecord returns the project name to use for the network based on the supplied project. +// If the project supplied has the "features.networks" flag enabled then the project name is returned, +// otherwise the default project name is returned. +func NetworkProjectFromRecord(p *api.Project) string { // Networks only use the project specified if the project has the features.networks feature enabled, // otherwise the legacy behaviour of using the default project for networks is used. - if shared.IsTrue(project.Config["features.networks"]) { - return projectName, project.Config, nil + if shared.IsTrue(p.Config["features.networks"]) { + return p.Name } - return Default, nil, nil + return Default } From b6c2647d92e2278444bd4201afc5eeb79bdf575a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 28 Oct 2020 17:38:16 +0000 Subject: [PATCH 3/6] Applying: lxd/storage/utils: project.StorageVolumeProjectFromRecord usage Signed-off-by: Thomas Parrott --- lxd/storage/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go index d4f40b0dc4..e98ff029bf 100644 --- a/lxd/storage/utils.go +++ b/lxd/storage/utils.go @@ -701,7 +701,7 @@ func VolumeUsedByInstances(s *state.State, poolName string, projectName string, volumeNameWithType := fmt.Sprintf("%s/%s", volumeTypeName, volumeName) return s.Cluster.InstanceList(func(inst db.Instance, p api.Project, profiles []api.Profile) error { - instStorageProject, err := project.StorageVolumeProjectFromRecord(&p, volumeType) + instStorageProject := project.StorageVolumeProjectFromRecord(&p, volumeType) if err != nil { return err } From 8c3aae2040633ca921f1d362554ce8bc9913a962 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 28 Oct 2020 17:38:43 +0000 Subject: [PATCH 4/6] lxd/network/driver/ovn: Adds NIC external route overlap validation of other OVN external network subnets and OVN NIC external routes Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 193 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 185 insertions(+), 8 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index bfda8d7ba7..81f3ebf7a7 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -17,6 +17,8 @@ import ( "github.com/lxc/lxd/lxd/cluster" "github.com/lxc/lxd/lxd/db" + deviceConfig "github.com/lxc/lxd/lxd/device/config" + "github.com/lxc/lxd/lxd/instance" "github.com/lxc/lxd/lxd/locking" "github.com/lxc/lxd/lxd/network/openvswitch" "github.com/lxc/lxd/lxd/project" @@ -1869,12 +1871,10 @@ func (n *ovn) getInstanceDevicePortName(instanceID int, deviceName string) openv } // InstanceDevicePortValidateExternalRoutes validates the external routes for an OVN instance port. -func (n *ovn) InstanceDevicePortValidateExternalRoutes(externalRoutes []*net.IPNet) error { - // Load the project to get uplink network restrictions. - p, err := n.state.Cluster.GetProject(n.project) - if err != nil { - return errors.Wrapf(err, "Failed to load network restrictions from project %q", n.project) - } +func (n *ovn) InstanceDevicePortValidateExternalRoutes(deviceInstance instance.Instance, deviceName string, portExternalRoutes []*net.IPNet) error { + var err error + var p *api.Project + var projectNetworks map[string]map[int64]api.Network // Get uplink routes. uplinkRoutes, err := n.uplinkRoutes(n.config["network"]) @@ -1882,19 +1882,91 @@ func (n *ovn) InstanceDevicePortValidateExternalRoutes(externalRoutes []*net.IPN return err } + err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error { + // Load the project to get uplink network restrictions. + p, err = tx.GetProject(n.project) + if err != nil { + return errors.Wrapf(err, "Failed to load network restrictions from project %q", n.project) + } + + // Get all managed networks across all projects. + projectNetworks, err = tx.GetNonPendingNetworks() + if err != nil { + return errors.Wrapf(err, "Failed to load all networks") + } + + return nil + }) + if err != nil { + return err + } + + // Work out which OVN networks (in all projects and including our own) use the same uplink as us. + ovnProjectNetworksWithOurUplink := make(map[string][]*api.Network) + for netProject, networks := range projectNetworks { + for _, network := range networks { + netInfo := network // Local var for adding pointer to ovnProjectNetworksWithOurUplink. + // Skip non-OVN networks or those networks that don't use the same uplink as us. + if netInfo.Type != "ovn" || netInfo.Config["network"] != n.config["network"] { + continue + } + + if ovnProjectNetworksWithOurUplink[netProject] == nil { + ovnProjectNetworksWithOurUplink[netProject] = []*api.Network{&netInfo} + } else { + ovnProjectNetworksWithOurUplink[netProject] = append(ovnProjectNetworksWithOurUplink[netProject], &netInfo) + } + } + } + + // Get external subnets used by other OVN networks using our uplink. + ovnNetworkExternalSubnets, err := n.ovnNetworkExternalSubnets(ovnProjectNetworksWithOurUplink, uplinkRoutes) + if err != nil { + return err + } + // Get project restricted routes. projectRestrictedSubnets, err := n.projectRestrictedSubnets(p, n.config["network"]) if err != nil { return err } - for _, externalRoute := range externalRoutes { - err = n.validateExternalSubnet(uplinkRoutes, projectRestrictedSubnets, externalRoute) + // If validating with an instance, get external routes configured on OVN NICs (excluding ours) using + // networks that use our uplink. + var ovnNICExternalRoutes []*net.IPNet + if deviceInstance != nil { + ovnNICExternalRoutes, err = n.ovnNICExternalRoutes(deviceInstance, deviceName, ovnProjectNetworksWithOurUplink) if err != nil { return err } } + for _, portExternalRoute := range portExternalRoutes { + // Check the external port route is allowed within both the uplink's external routes and any + // project restricted subnets. + err = n.validateExternalSubnet(uplinkRoutes, projectRestrictedSubnets, portExternalRoute) + if err != nil { + return err + } + + // Check the external port route doesn't fall within any existing OVN network external subnets. + for _, ovnNetworkExternalSubnet := range ovnNetworkExternalSubnets { + if SubnetContains(ovnNetworkExternalSubnet, portExternalRoute) || SubnetContains(portExternalRoute, ovnNetworkExternalSubnet) { + // This error is purposefully vague so that it doesn't reveal any names of + // resources potentially outside of the NIC's project. + return fmt.Errorf("External route %q overlaps with another OVN network's external subnet", portExternalRoute.String()) + } + } + + for _, ovnNICExternalRoute := range ovnNICExternalRoutes { + if SubnetContains(ovnNICExternalRoute, portExternalRoute) || SubnetContains(portExternalRoute, ovnNICExternalRoute) { + // This error is purposefully vague so that it doesn't reveal any names of + // resources potentially outside of the NIC's project. + return fmt.Errorf("External route %q overlaps with another OVN NIC's external route", portExternalRoute.String()) + } + } + } + return nil } @@ -2187,3 +2259,108 @@ func (n *ovn) DHCPv6Subnet() *net.IPNet { return subnet } + +// ovnNetworkExternalSubnets returns a list of external subnets used by OVN networks (including our own) using the +// same uplink as this OVN network. OVN networks are considered to be using external subnets if their ipv4.address +// and/or ipv6.address are in the uplink's external routes and the associated NAT is disabled for the IP family. +func (n *ovn) ovnNetworkExternalSubnets(tx *db.ClusterTx, uplinkRoutes []*net.IPNet) ([]*net.IPNet, error) { + // Get all managed networks across all projects. + projectNetworks, err := tx.GetNonPendingNetworks() + if err != nil { + return nil, errors.Wrapf(err, "Failed to load all networks") + } + + externalSubnets := make([]*net.IPNet, 0) + for _, networks := range projectNetworks { + for _, netInfo := range networks { + // Skip non-OVN networks or those networks that don't use the same uplink as us. + if netInfo.Type != "ovn" || netInfo.Config["network"] != n.config["network"] { + continue + } + + for _, keyPrefix := range []string{"ipv4", "ipv6"} { + if !shared.IsTrue(netInfo.Config[fmt.Sprintf("%s.nat", keyPrefix)]) { + _, ipNet, _ := net.ParseCIDR(netInfo.Config[fmt.Sprintf("%s.address", keyPrefix)]) + if ipNet == nil { + // If the network doesn't have a valid IP, skip it. + continue + } + + // Check the network's subnet is a valid external route on uplink. + err = n.validateExternalSubnet(uplinkRoutes, nil, ipNet) + if err != nil { + return nil, errors.Wrapf(err, "Failed checking if OVN network external subnet %q is valid external route on uplink %q", ipNet.String(), n.config["network"]) + } + + externalSubnets = append(externalSubnets, ipNet) + } + } + } + } + + return externalSubnets, nil +} + +// ovnNICExternalRoutes returns a list of external routes currently used by OVN NICs (excluding our own) that are +// connected to OVN networks that share the same uplink as this network uses. +func (n *ovn) ovnNICExternalRoutes(ourDeviceInstance instance.Instance, ourDeviceName string, ovnProjectNetworksWithOurUplink map[string][]*api.Network) ([]*net.IPNet, error) { + externalRoutes := make([]*net.IPNet, 0) + + // nicUsesNetwork returns true if the nicDev's "network" property matches one of the projectNetworks names + // and the instNetworkProject matches the projectNetworks's project. As we only use network name and + // project to match we rely on projectNetworks only including OVN networks that use our uplink. + nicUsesNetwork := func(instNetworkProject string, nicDev map[string]string, projectNetworks map[string][]*api.Network) bool { + for netProject, networks := range projectNetworks { + for _, network := range networks { + if netProject == instNetworkProject && network.Name == nicDev["network"] { + return true + } + } + } + + return false + } + + err := n.state.Cluster.InstanceList(func(inst db.Instance, p api.Project, profiles []api.Profile) error { + // Get the instance's effective network project name. + instNetworkProject := project.NetworkProjectFromRecord(&p) + devices := db.ExpandInstanceDevices(deviceConfig.NewDevices(inst.Devices), profiles).CloneNative() + + // Iterate through each of the instance's devices, looking for OVN NICs that are linked to networks + // that use our uplink. + for devName, devConfig := range devices { + if devConfig["type"] != "nic" { + continue + } + + // Skip our own device. + if ourDeviceInstance != nil && inst.Name == ourDeviceInstance.Name() && inst.Project == ourDeviceInstance.Project() && ourDeviceName == devName { + continue + } + + // Check whether the NIC device references one of the OVN networks supplied. + if !nicUsesNetwork(instNetworkProject, devConfig, ovnProjectNetworksWithOurUplink) { + continue + } + + // For OVN NICs that are connected to networks that use the same uplink as we do, check + // if they have any external routes configured, and if so add them to the list to return. + for _, keyPrefix := range []string{"ipv4", "ipv6"} { + _, ipNet, _ := net.ParseCIDR(devConfig[fmt.Sprintf("%s.routes.external", keyPrefix)]) + if ipNet == nil { + // If the NIC device doesn't have a valid external route setting, skip. + continue + } + + externalRoutes = append(externalRoutes, ipNet) + } + } + + return nil + }) + if err != nil { + return nil, err + } + + return externalRoutes, nil +} From e9e411a061695d23245eeed25742b3d8ba1e9e21 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 28 Oct 2020 17:39:26 +0000 Subject: [PATCH 5/6] lxd/device/nic/ovn: Updates ovnNet interface's InstanceDevicePortValidateExternalRoutes to add instance argument Signed-off-by: Thomas Parrott --- lxd/device/nic_ovn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go index f743083165..5e86e662ba 100644 --- a/lxd/device/nic_ovn.go +++ b/lxd/device/nic_ovn.go @@ -28,7 +28,7 @@ import ( type ovnNet interface { network.Network - InstanceDevicePortValidateExternalRoutes(externalRoutes []*net.IPNet) error + InstanceDevicePortValidateExternalRoutes(deviceInstance instance.Instance, deviceName string, externalRoutes []*net.IPNet) error InstanceDevicePortAdd(instanceID int, instanceName string, deviceName string, mac net.HardwareAddr, ips []net.IP, internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) (openvswitch.OVNSwitchPort, error) InstanceDevicePortDelete(instanceID int, deviceName string, internalRoutes []*net.IPNet, externalRoutes []*net.IPNet) error InstanceDevicePortDynamicIPs(instanceID int, deviceName string) ([]net.IP, error) From c47c3ecc793876764e21e7531c2c5ff4b31ce99a Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Wed, 28 Oct 2020 17:40:07 +0000 Subject: [PATCH 6/6] lxd/device/nic/ovn: d.network.InstanceDevicePortValidateExternalRoutes usage Signed-off-by: Thomas Parrott --- lxd/device/nic_ovn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go index 5e86e662ba..5eb94e5125 100644 --- a/lxd/device/nic_ovn.go +++ b/lxd/device/nic_ovn.go @@ -179,7 +179,7 @@ func (d *nicOVN) validateConfig(instConf instance.ConfigReader) error { } } - err = d.network.InstanceDevicePortValidateExternalRoutes(externalRoutes) + err = d.network.InstanceDevicePortValidateExternalRoutes(d.inst, d.name, externalRoutes) if err != nil { return err } From builds at travis-ci.org Wed Oct 28 18:40:57 2020 From: builds at travis-ci.org (Travis CI) Date: Wed, 28 Oct 2020 18:40:57 +0000 Subject: [lxc-devel] Errored: lxc/lxc#7931 (master - 7fde74f) In-Reply-To: Message-ID: <5f99bb37b0925_13fc582ac8dac16823c@travis-tasks-6646766c48-l6p2f.mail> Build Update for lxc/lxc ------------------------------------- Build: #7931 Status: Errored Duration: 6 hrs, 37 mins, and 49 secs Commit: 7fde74f (master) Author: Stéphane Graber Message: Merge pull request #3568 from brauner/2020-10-28/fixes coverity fixes View the changeset: https://github.com/lxc/lxc/compare/bf0b9c1ed6a9...7fde74f3759c View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/739588360?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lxc-bot at linuxcontainers.org Wed Oct 28 20:13:40 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Wed, 28 Oct 2020 13:13:40 -0700 (PDT) Subject: [lxc-devel] [lxc-ci/master] images/pld: Fix networking Message-ID: <5f99d0f4.1c69fb81.88163.b93eSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 303 bytes Desc: not available URL: -------------- next part -------------- From 64dba4242acb442e148523fd84779196733b9342 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Wed, 28 Oct 2020 21:12:26 +0100 Subject: [PATCH] images/pld: Fix networking /etc/rc.d/init.d/network is trying to use /sbin/ip which doesn't exist. The ip command is in /bin. This creates a symlink in /sbin which solves the problem. --- images/pld.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/images/pld.yaml b/images/pld.yaml index 6b5dd33..7a6d249 100644 --- a/images/pld.yaml +++ b/images/pld.yaml @@ -152,6 +152,8 @@ actions: # uninstall needless systemd-units rpm -q systemd-units >/dev/null && rpm -e systemd-units --nodeps --noscripts + ln -s /bin/ip /sbin/ip + # cleanup rpm -e localedb-src rm -f /var/cache/hrmib/* From lxc-bot at linuxcontainers.org Wed Oct 28 21:44:49 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Wed, 28 Oct 2020 14:44:49 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/instance/qmp: Merge Go routines Message-ID: <5f99e651.1c69fb81.bec03.f466SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From d735de0a151eda78d7f73b157f91789ad38d396e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 28 Oct 2020 17:44:24 -0400 Subject: [PATCH] lxd/instance/qmp: Merge Go routines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/instance/drivers/qmp/monitor.go | 72 +++++++++++++++-------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/lxd/instance/drivers/qmp/monitor.go b/lxd/instance/drivers/qmp/monitor.go index e02bc96590..3e4d3d1dd8 100644 --- a/lxd/instance/drivers/qmp/monitor.go +++ b/lxd/instance/drivers/qmp/monitor.go @@ -75,47 +75,40 @@ func Connect(path string, serialCharDev string, eventHandler func(name string, d } func (m *Monitor) run() error { - // Start ringbuffer monitoring go routine. - go func() { - for { - // Read the ringbuffer. - resp, err := m.qmp.Run([]byte(fmt.Sprintf(`{"execute": "ringbuf-read", "arguments": {"device": "%s", "size": %d, "format": "utf8"}}`, m.serialCharDev, RingbufSize))) - if err != nil { - m.Disconnect() - return - } - - // Decode the response. - var respDecoded struct { - Return string `json:"return"` - } + // Ringbuffer monitoring function. + checkBuffer := func() { + // Read the ringbuffer. + resp, err := m.qmp.Run([]byte(fmt.Sprintf(`{"execute": "ringbuf-read", "arguments": {"device": "%s", "size": %d, "format": "utf8"}}`, m.serialCharDev, RingbufSize))) + if err != nil { + // Failure to send a command, assume disconnected/crashed. + m.Disconnect() + return + } - err = json.Unmarshal(resp, &respDecoded) - if err != nil { - continue - } + // Decode the response. + var respDecoded struct { + Return string `json:"return"` + } - // Extract the last entry. - entries := strings.Split(respDecoded.Return, "\n") - if len(entries) > 1 { - status := entries[len(entries)-2] + err = json.Unmarshal(resp, &respDecoded) + if err != nil { + // Received bad data, assume disconnected/crashed. + m.Disconnect() + return + } - if status == "STARTED" { - m.agentReady = true - } else if status == "STOPPED" { - m.agentReady = false - } - } + // Extract the last entry. + entries := strings.Split(respDecoded.Return, "\n") + if len(entries) > 1 { + status := entries[len(entries)-2] - // Wait until next read or cancel. - select { - case <-m.chDisconnect: - return - case <-time.After(10 * time.Second): - continue + if status == "STARTED" { + m.agentReady = true + } else if status == "STOPPED" { + m.agentReady = false } } - }() + } // Start event monitoring go routine. chEvents, err := m.qmp.Events() @@ -124,7 +117,11 @@ func (m *Monitor) run() error { } go func() { + // Initial read from the ringbuffer. + go checkBuffer() + for { + // Wait for an event, disconnection or timeout. select { case <-m.chDisconnect: return @@ -136,6 +133,11 @@ func (m *Monitor) run() error { if m.eventHandler != nil { m.eventHandler(e.Event, e.Data) } + case <-time.After(10 * time.Second): + // Check if the ringbuffer was updated (non-blocking). + go checkBuffer() + + continue } } }() From lxc-bot at linuxcontainers.org Wed Oct 28 23:06:59 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Wed, 28 Oct 2020 16:06:59 -0700 (PDT) Subject: [lxc-devel] [lxd/master] canceler: Fix goroutine leak on failure Message-ID: <5f99f993.1c69fb81.24969.0b03SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 365d4e97556e10d7997502dbaeb5de78bc4a4d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 28 Oct 2020 18:48:04 -0400 Subject: [PATCH 1/2] shared/cancel: Close chDone on failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- shared/cancel/canceler.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/shared/cancel/canceler.go b/shared/cancel/canceler.go index b3356cf37e..51263b6020 100644 --- a/shared/cancel/canceler.go +++ b/shared/cancel/canceler.go @@ -69,5 +69,10 @@ func CancelableDownload(c *Canceler, client *http.Client, req *http.Request) (*h }() resp, err := client.Do(req) - return resp, chDone, err + if err != nil { + close(chDone) + return nil, nil, err + } + + return resp, chDone, nil } From 5dbef17cb415de8e3ef5acb3349ae4b77e61ceb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 28 Oct 2020 18:48:20 -0400 Subject: [PATCH 2/2] lxd: Only close doneCh on success MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/daemon_images.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/daemon_images.go b/lxd/daemon_images.go index 7e272a3a89..3a5b244d7e 100644 --- a/lxd/daemon_images.go +++ b/lxd/daemon_images.go @@ -401,10 +401,10 @@ func (d *Daemon) ImageDownload(op *operations.Operation, server string, protocol // Make the request raw, doneCh, err := cancel.CancelableDownload(canceler, httpClient, req) - defer close(doneCh) if err != nil { return nil, err } + defer close(doneCh) if raw.StatusCode != http.StatusOK { return nil, fmt.Errorf("Unable to fetch %q: %s", server, raw.Status) From builds at travis-ci.org Thu Oct 29 08:11:38 2020 From: builds at travis-ci.org (Travis CI) Date: Thu, 29 Oct 2020 08:11:38 +0000 Subject: [lxc-devel] Canceled: lxc/lxc#7928 (master - bf0b9c1) In-Reply-To: Message-ID: <5f9a7938362be_13ff5ec5bfa3c222226@travis-tasks-6cf7c5c58f-djctf.mail> Build Update for lxc/lxc ------------------------------------- Build: #7928 Status: Canceled Duration: 6 hrs, 52 mins, and 13 secs Commit: bf0b9c1 (master) Author: Christian Brauner Message: Merge pull request #3567 from blenk92/lxc-attach-selinux lxc-attach: Enable setting the SELinux context View the changeset: https://github.com/lxc/lxc/compare/a093bb0f5c3d...bf0b9c1ed6a9 View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/739353532?utm_medium=notification&utm_source=email Restart your build: https://travis-ci.org/github/lxc/lxc/builds/739353532?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From builds at travis-ci.org Thu Oct 29 08:12:23 2020 From: builds at travis-ci.org (Travis CI) Date: Thu, 29 Oct 2020 08:12:23 +0000 Subject: [lxc-devel] Canceled: lxc/lxc#7931 (master - 7fde74f) In-Reply-To: Message-ID: <5f9a7968e2f5e_13ff5ec5bf1cc2308d3@travis-tasks-6cf7c5c58f-djctf.mail> Build Update for lxc/lxc ------------------------------------- Build: #7931 Status: Canceled Duration: 6 hrs, 37 mins, and 49 secs Commit: 7fde74f (master) Author: Stéphane Graber Message: Merge pull request #3568 from brauner/2020-10-28/fixes coverity fixes View the changeset: https://github.com/lxc/lxc/compare/bf0b9c1ed6a9...7fde74f3759c View the full build log and details: https://travis-ci.org/github/lxc/lxc/builds/739588360?utm_medium=notification&utm_source=email Restart your build: https://travis-ci.org/github/lxc/lxc/builds/739588360?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the lxc/lxc repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=1693277&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lxc-bot at linuxcontainers.org Thu Oct 29 11:07:34 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Thu, 29 Oct 2020 04:07:34 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Network: Adds external subnet overlap validation for OVN networks Message-ID: <5f9aa276.1c69fb81.1c251.11b3SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From f899806b77a562de10764e8f52afb0ac1d6d52fd Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 29 Oct 2020 11:03:01 +0000 Subject: [PATCH 1/5] lxd/network/driver/ovn: Adds ovnProjectNetworksWithUplink function Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 9ce25782e7..0f141d73e2 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -2353,3 +2353,27 @@ func (n *ovn) ovnNICExternalRoutes(ourDeviceInstance instance.Instance, ourDevic return externalRoutes, nil } + +// ovnProjectNetworksWithUplink accepts a map of all networks in all projects and returns a filtered map of OVN +// networks that use the uplink specified. +func (n *ovn) ovnProjectNetworksWithUplink(uplink string, projectNetworks map[string]map[int64]api.Network) map[string][]*api.Network { + ovnProjectNetworksWithOurUplink := make(map[string][]*api.Network) + for netProject, networks := range projectNetworks { + for _, ni := range networks { + network := ni // Local var creating pointer to rather than iterator. + + // Skip non-OVN networks or those networks that don't use the uplink specified. + if network.Type != "ovn" || network.Config["network"] != uplink { + continue + } + + if ovnProjectNetworksWithOurUplink[netProject] == nil { + ovnProjectNetworksWithOurUplink[netProject] = []*api.Network{&network} + } else { + ovnProjectNetworksWithOurUplink[netProject] = append(ovnProjectNetworksWithOurUplink[netProject], &network) + } + } + } + + return ovnProjectNetworksWithOurUplink +} From c6d6045b8f43cf749666eefe99e6c4fa0d36d2e9 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 29 Oct 2020 11:04:42 +0000 Subject: [PATCH 2/5] lxd/network/driver/ovn: Updates ovnNetworkExternalSubnets to allow optional filtering of our own network's subnets Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 0f141d73e2..cbfaa1f080 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -2260,13 +2260,18 @@ func (n *ovn) DHCPv6Subnet() *net.IPNet { return subnet } -// ovnNetworkExternalSubnets returns a list of external subnets used by OVN networks (including our own) using the -// same uplink as this OVN network. OVN networks are considered to be using external subnets if their ipv4.address -// and/or ipv6.address are in the uplink's external routes and the associated NAT is disabled for the IP family. -func (n *ovn) ovnNetworkExternalSubnets(ovnProjectNetworksWithOurUplink map[string][]*api.Network, uplinkRoutes []*net.IPNet) ([]*net.IPNet, error) { +// ovnNetworkExternalSubnets returns a list of external subnets used by OVN networks (optionally exluding our own +// if both ourProject and ourNetwork are non-empty) using the same uplink as this OVN network. OVN networks are +// considered to be using external subnets if their ipv4.address and/or ipv6.address are in the uplink's external +// routes and the associated NAT is disabled for the IP family. +func (n *ovn) ovnNetworkExternalSubnets(ourProject string, ourNetwork string, ovnProjectNetworksWithOurUplink map[string][]*api.Network, uplinkRoutes []*net.IPNet) ([]*net.IPNet, error) { externalSubnets := make([]*net.IPNet, 0) - for _, networks := range ovnProjectNetworksWithOurUplink { + for netProject, networks := range ovnProjectNetworksWithOurUplink { for _, netInfo := range networks { + if netProject == ourProject && netInfo.Name == ourNetwork { + continue + } + for _, keyPrefix := range []string{"ipv4", "ipv6"} { if !shared.IsTrue(netInfo.Config[fmt.Sprintf("%s.nat", keyPrefix)]) { _, ipNet, _ := net.ParseCIDR(netInfo.Config[fmt.Sprintf("%s.address", keyPrefix)]) From 7872cdf100bfb56814b20357c8d869da194d6db3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 29 Oct 2020 11:05:15 +0000 Subject: [PATCH 3/5] lxd/network/driver/ovn: Updates ovnNICExternalRoutes to optionally filter our own NIC's external routes Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index cbfaa1f080..c44d32547a 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -2295,8 +2295,9 @@ func (n *ovn) ovnNetworkExternalSubnets(ourProject string, ourNetwork string, ov return externalSubnets, nil } -// ovnNICExternalRoutes returns a list of external routes currently used by OVN NICs (excluding our own) that are -// connected to OVN networks that share the same uplink as this network uses. +// ovnNICExternalRoutes returns a list of external routes currently used by OVN NICs (optionally excluding our +// own if both ourDeviceInstance and ourDeviceName are non-empty) that are connected to OVN networks that share +// the same uplink as this network uses. func (n *ovn) ovnNICExternalRoutes(ourDeviceInstance instance.Instance, ourDeviceName string, ovnProjectNetworksWithOurUplink map[string][]*api.Network) ([]*net.IPNet, error) { externalRoutes := make([]*net.IPNet, 0) @@ -2327,8 +2328,8 @@ func (n *ovn) ovnNICExternalRoutes(ourDeviceInstance instance.Instance, ourDevic continue } - // Skip our own device. - if inst.Name == ourDeviceInstance.Name() && inst.Project == ourDeviceInstance.Project() && ourDeviceName == devName { + // Skip our own device (if instance and device name were supplied). + if ourDeviceInstance != nil && ourDeviceName != "" && inst.Name == ourDeviceInstance.Name() && inst.Project == ourDeviceInstance.Project() && ourDeviceName == devName { continue } From f1f9e0a46909c4c88c278d0e76fee077d3eb4df2 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 29 Oct 2020 11:05:57 +0000 Subject: [PATCH 4/5] lxd/network/driver/ovn: Updates InstanceDevicePortValidateExternalRoutes to use new functions and signatures Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index c44d32547a..59a885595e 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1901,26 +1901,11 @@ func (n *ovn) InstanceDevicePortValidateExternalRoutes(deviceInstance instance.I return err } - // Work out which OVN networks (in all projects and including our own) use the same uplink as us. - ovnProjectNetworksWithOurUplink := make(map[string][]*api.Network) - for netProject, networks := range projectNetworks { - for _, network := range networks { - netInfo := network // Local var for adding pointer to ovnProjectNetworksWithOurUplink. - // Skip non-OVN networks or those networks that don't use the same uplink as us. - if netInfo.Type != "ovn" || netInfo.Config["network"] != n.config["network"] { - continue - } - - if ovnProjectNetworksWithOurUplink[netProject] == nil { - ovnProjectNetworksWithOurUplink[netProject] = []*api.Network{&netInfo} - } else { - ovnProjectNetworksWithOurUplink[netProject] = append(ovnProjectNetworksWithOurUplink[netProject], &netInfo) - } - } - } + // Get OVN networks that use the same uplink as us. + ovnProjectNetworksWithOurUplink := n.ovnProjectNetworksWithUplink(n.config["network"], projectNetworks) // Get external subnets used by other OVN networks using our uplink. - ovnNetworkExternalSubnets, err := n.ovnNetworkExternalSubnets(ovnProjectNetworksWithOurUplink, uplinkRoutes) + ovnNetworkExternalSubnets, err := n.ovnNetworkExternalSubnets("", "", ovnProjectNetworksWithOurUplink, uplinkRoutes) if err != nil { return err } @@ -1931,14 +1916,10 @@ func (n *ovn) InstanceDevicePortValidateExternalRoutes(deviceInstance instance.I return err } - // If validating with an instance, get external routes configured on OVN NICs (excluding ours) using - // networks that use our uplink. - var ovnNICExternalRoutes []*net.IPNet - if deviceInstance != nil { - ovnNICExternalRoutes, err = n.ovnNICExternalRoutes(deviceInstance, deviceName, ovnProjectNetworksWithOurUplink) - if err != nil { - return err - } + // Get external routes configured on OVN NICs (excluding ours) using networks that use our uplink. + ovnNICExternalRoutes, err := n.ovnNICExternalRoutes(deviceInstance, deviceName, ovnProjectNetworksWithOurUplink) + if err != nil { + return err } for _, portExternalRoute := range portExternalRoutes { From 5a53d16c5db5ef98c3fe13682ca50ee0c9caf810 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 29 Oct 2020 11:03:34 +0000 Subject: [PATCH 5/5] lxd/network/driver/ovn: Updates Validate to check external subnets dont overlap with other OVN networks or NICs sharing our uplink Signed-off-by: Thomas Parrott --- lxd/network/driver_ovn.go | 58 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 59a885595e..de2f32024f 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -230,7 +230,8 @@ func (n *ovn) Validate(config map[string]string) error { return err } - // If NAT disabled, check subnets are within the uplink network's routes and project's subnet restrictions. + // If NAT disabled, parse the external subnets that are being requested. + var externalSubnets []*net.IPNet for _, keyPrefix := range []string{"ipv4", "ipv6"} { if !shared.IsTrue(config[fmt.Sprintf("%s.nat", keyPrefix)]) && validate.IsOneOf(config[fmt.Sprintf("%s.address", keyPrefix)], []string{"", "none", "auto"}) != nil { _, ipNet, err := net.ParseCIDR(config[fmt.Sprintf("%s.address", keyPrefix)]) @@ -238,10 +239,63 @@ func (n *ovn) Validate(config map[string]string) error { return err } - err = n.validateExternalSubnet(uplinkRoutes, projectRestrictedSubnets, ipNet) + externalSubnets = append(externalSubnets, ipNet) + } + } + + if len(externalSubnets) > 0 { + var projectNetworks map[string]map[int64]api.Network + err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error { + // Get all managed networks across all projects. + projectNetworks, err = tx.GetNonPendingNetworks() + if err != nil { + return errors.Wrapf(err, "Failed to load all networks") + } + + return nil + }) + if err != nil { + return err + } + + // Get OVN networks that use the same uplink as us. + ovnProjectNetworksWithOurUplink := n.ovnProjectNetworksWithUplink(config["network"], projectNetworks) + + // Get external subnets used by other OVN networks using our uplink. + ovnNetworkExternalSubnets, err := n.ovnNetworkExternalSubnets(n.project, n.name, ovnProjectNetworksWithOurUplink, uplinkRoutes) + if err != nil { + return err + } + + // Get external routes configured on OVN NICs using networks that use our uplink. + ovnNICExternalRoutes, err := n.ovnNICExternalRoutes(nil, "", ovnProjectNetworksWithOurUplink) + if err != nil { + return err + } + + // Check external subnets are within the uplink network's routes and project's subnet restrictions. + for _, externalSubnet := range externalSubnets { + err = n.validateExternalSubnet(uplinkRoutes, projectRestrictedSubnets, externalSubnet) if err != nil { return err } + + // Check the external port route doesn't fall within any existing OVN network external subnets. + for _, ovnNetworkExternalSubnet := range ovnNetworkExternalSubnets { + if SubnetContains(ovnNetworkExternalSubnet, externalSubnet) || SubnetContains(externalSubnet, ovnNetworkExternalSubnet) { + // This error is purposefully vague so that it doesn't reveal any names of + // resources potentially outside of the network's project. + return fmt.Errorf("External subnet %q overlaps with another OVN network's external subnet", externalSubnet.String()) + } + } + + for _, ovnNICExternalRoute := range ovnNICExternalRoutes { + if SubnetContains(ovnNICExternalRoute, externalSubnet) || SubnetContains(externalSubnet, ovnNICExternalRoute) { + // This error is purposefully vague so that it doesn't reveal any names of + // resources potentially outside of the networks's project. + return fmt.Errorf("External subnet %q overlaps with another OVN NIC's external route", externalSubnet.String()) + } + } } } From lxc-bot at linuxcontainers.org Thu Oct 29 13:13:03 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Thu, 29 Oct 2020 06:13:03 -0700 (PDT) Subject: [lxc-devel] [lxc-ci/master] images/fedora: Change kernelopts for Fedora 33 Message-ID: <5f9abfdf.1c69fb81.47bf2.1f00SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 303 bytes Desc: not available URL: -------------- next part -------------- From d1fde0451110877024fb442c73dc1f07b8c8aeed Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Thu, 29 Oct 2020 14:12:35 +0100 Subject: [PATCH] images/fedora: Change kernelopts for Fedora 33 This fixes the issue where a Fedora 33 VM wouldn't boot due to incorrect kernel opts. Signed-off-by: Thomas Hipp --- images/fedora.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/images/fedora.yaml b/images/fedora.yaml index f4261bb..95c002b 100644 --- a/images/fedora.yaml +++ b/images/fedora.yaml @@ -293,6 +293,17 @@ actions: types: - vm +- trigger: post-files + action: |- + #!/bin/sh + set -eux + + sed -ri 's#^options .+#options $kernelopts#g' /boot/loader/entries/* + types: + - vm + releases: + - 33 + - trigger: post-files action: |- #!/bin/sh From lxc-bot at linuxcontainers.org Thu Oct 29 13:43:29 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Thu, 29 Oct 2020 06:43:29 -0700 (PDT) Subject: [lxc-devel] [lxc-ci/master] bin/test-lxd-ovn: Adds external subnets and routes overlap checks Message-ID: <5f9ac701.1c69fb81.2fe4a.3973SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 414 bytes Desc: not available URL: -------------- next part -------------- From 02bceb0499651a10c9add213a12e4255a2e746f9 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Thu, 29 Oct 2020 13:42:37 +0000 Subject: [PATCH] bin/test-lxd-ovn: Adds external subnets and routes overlap checks Signed-off-by: Thomas Parrott --- bin/test-lxd-ovn | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/bin/test-lxd-ovn b/bin/test-lxd-ovn index a2844cd..f011144 100755 --- a/bin/test-lxd-ovn +++ b/bin/test-lxd-ovn @@ -151,6 +151,13 @@ lxc network create ovn-virtual-network --type=ovn --project testovn network=dumm ipv4.nat=false \ ipv6.nat=false +# Check network external subnet overlap. +! lxc network create ovn-virtual-network2 --type=ovn --project default network=dummy \ + ipv4.address=198.51.100.1/26 \ + ipv6.address=2001:db8:1:2::1/122 \ + ipv4.nat=false \ + ipv6.nat=false || false + lxc init images:ubuntu/20.04 u1 --project testovn lxc config device add u1 eth0 nic network=ovn-virtual-network name=eth0 --project testovn lxc start u1 --project testovn @@ -170,10 +177,20 @@ lxc network set ovn-virtual-network --project testovn \ ipv4.nat=true \ ipv6.nat=true +# Check external routes are ensured to be within uplink's external routes. ! lxc config device set u1 eth0 ipv4.routes.external=198.51.100.0/24 --project testovn || false ! lxc config device set u1 eth0 ipv6.routes.external=2001:db8:1:2::/64 --project testovn || false lxc config device set u1 eth0 ipv4.routes.external=198.51.100.0/26 --project testovn lxc config device set u1 eth0 ipv6.routes.external=2001:db8:1:2::/122 --project testovn + +# Check NIC external route overlap detection. +lxc init images:ubuntu/20.04 u2 --project testovn +lxc config device add u2 eth0 nic network=ovn-virtual-network name=eth0 --project testovn +! lxc config device set u2 eth0 ipv4.routes.external=198.51.100.1/32 --project testovn || false +! lxc config device set u2 eth0 ipv6.routes.external=2001:db8:1:2::1/128 --project testovn || false +lxc delete -f u2 --project testovn + +# Check DNAT rules get added when starting instance port with external routes. lxc start u1 --project testovn ovn-nbctl --bare --format=csv --column=external_ip,logical_ip,type find nat ovn-nbctl --bare --format=csv --column=external_ip,logical_ip,type find nat | grep "198.51.100.0,198.51.100.0,dnat_and_snat" From lxc-bot at linuxcontainers.org Thu Oct 29 19:18:45 2020 From: lxc-bot at linuxcontainers.org (brauner on Github) Date: Thu, 29 Oct 2020 12:18:45 -0700 (PDT) Subject: [lxc-devel] [lxd/master] exec: make sure to only use TIOCGPTPEER if available Message-ID: <5f9b1595.1c69fb81.2cd3f.7a39SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 504 bytes Desc: not available URL: -------------- next part -------------- From 0814fd4d1145147f09314b64bd9b9740acb3be79 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 29 Oct 2020 20:17:17 +0100 Subject: [PATCH] exec: make sure to only use TIOCGPTPEER if available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://discuss.linuxcontainers.org/t/can-not-start-bash-on-a-container/9315 Signed-off-by: Stéphane Graber Signed-off-by: Christian Brauner --- lxd/instance_exec.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lxd/instance_exec.go b/lxd/instance_exec.go index 23f7ddbc2d..76db1bfaee 100644 --- a/lxd/instance_exec.go +++ b/lxd/instance_exec.go @@ -21,6 +21,7 @@ import ( "github.com/lxc/lxd/lxd/instance/instancetype" "github.com/lxc/lxd/lxd/operations" "github.com/lxc/lxd/lxd/response" + "github.com/lxc/lxd/lxd/state" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" log "github.com/lxc/lxd/shared/log15" @@ -44,6 +45,7 @@ type execWs struct { controlConnectedDone bool fds map[int]string devptsFd *os.File + s *state.State } func (s *execWs) Metadata() interface{} { @@ -133,7 +135,7 @@ func (s *execWs) Do(op *operations.Operation) error { ttys = make([]*os.File, 1) ptys = make([]*os.File, 1) - if s.devptsFd != nil { + if s.devptsFd != nil && s.s.OS.NativeTerminals { ptys[0], ttys[0], err = shared.OpenPtyInDevpts(int(s.devptsFd.Fd()), s.rootUid, s.rootGid) s.devptsFd.Close() s.devptsFd = nil @@ -504,6 +506,7 @@ func containerExecPost(d *Daemon, r *http.Request) response.Response { if post.WaitForWS { ws := &execWs{} + ws.s = d.State() ws.fds = map[int]string{} if inst.Type() == instancetype.Container { From lxc-bot at linuxcontainers.org Fri Oct 30 01:55:19 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Thu, 29 Oct 2020 18:55:19 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/instances: Fix virtiofsd for config drive Message-ID: <5f9b7287.1c69fb81.e436e.6e05SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 354 bytes Desc: not available URL: -------------- next part -------------- From db04ac666b7ecef2ef0a2d8b3bccded8004f4fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 29 Oct 2020 21:47:57 -0400 Subject: [PATCH] lxd/instances: Fix virtiofsd for config drive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/instance/drivers/driver_qemu.go | 46 +++++++++++++++-------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 1de732a6d2..8b59e1ea5b 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -884,35 +884,37 @@ func (vm *qemu) Start(stateful bool) error { } } - // Start the virtiofsd process in non-daemon mode. - if cmd != "" { - proc, err := subprocess.NewProcess("/usr/lib/qemu/virtiofsd", []string{fmt.Sprintf("--socket-path=%s", sockPath), "-o", fmt.Sprintf("source=%s", filepath.Join(vm.Path(), "config"))}, "", "") - if err != nil { - return err - } + if cmd == "" { + return fmt.Errorf("Required binary 'virtiofsd' couldn't be found") + } - err = proc.Start() - if err != nil { - return err - } + // Start the virtiofsd process in non-daemon mode. + proc, err := subprocess.NewProcess(cmd, []string{fmt.Sprintf("--socket-path=%s", sockPath), "-o", fmt.Sprintf("source=%s", filepath.Join(vm.Path(), "config"))}, "", "") + if err != nil { + return err + } - revert.Add(func() { proc.Stop() }) + err = proc.Start() + if err != nil { + return err + } - pidPath := filepath.Join(vm.LogPath(), "virtiofsd.pid") + revert.Add(func() { proc.Stop() }) - err = proc.Save(pidPath) - if err != nil { - return err - } + pidPath := filepath.Join(vm.LogPath(), "virtiofsd.pid") - // Wait for socket file to exist - for i := 0; i < 10; i++ { - if shared.PathExists(sockPath) { - break - } + err = proc.Save(pidPath) + if err != nil { + return err + } - time.Sleep(50 * time.Millisecond) + // Wait for socket file to exist + for i := 0; i < 10; i++ { + if shared.PathExists(sockPath) { + break } + + time.Sleep(50 * time.Millisecond) } // Setup background process. From lxc-bot at linuxcontainers.org Fri Oct 30 03:09:59 2020 From: lxc-bot at linuxcontainers.org (lxc-jp on Github) Date: Thu, 29 Oct 2020 20:09:59 -0700 (PDT) Subject: [lxc-devel] [linuxcontainers.org/master] Add Japanese release announcement of LXC 4.0.5 Message-ID: <5f9b8407.1c69fb81.20594.b41fSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 370 bytes Desc: not available URL: -------------- next part -------------- From 48970b502a4b85f689cd487c3b30202616e454aa Mon Sep 17 00:00:00 2001 From: KATOH Yasufumi Date: Fri, 30 Oct 2020 12:08:47 +0900 Subject: [PATCH] Add Japanese release announcement of LXC 4.0.5 Signed-off-by: KATOH Yasufumi --- content/lxc/news.ja/lxc-4.0.5.yaml | 86 ++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 content/lxc/news.ja/lxc-4.0.5.yaml diff --git a/content/lxc/news.ja/lxc-4.0.5.yaml b/content/lxc/news.ja/lxc-4.0.5.yaml new file mode 100644 index 0000000..c96fb7d --- /dev/null +++ b/content/lxc/news.ja/lxc-4.0.5.yaml @@ -0,0 +1,86 @@ +title: LXC 4.0.5 LTS has been released +date: 2020/10/22 17:10 +origin: https://discuss.linuxcontainers.org/t/lxc-4-0-5-lts-has-been-released/9269 +content: |- + ### はじめに + + LXC チームが LXC 4.0.5 のリリースをお知らせします! + + + このリリースは 2025 年 6 月までサポートされる LXC 4.0 に対する 5 回目のバグフィックスリリースです。 + + ### バグ修正(とハイライト) + + + - コンテナ内からの PTS デバイスの割り当てができるようになりました + - パス・マウント処理のロジックをより堅牢にしました + - イニシャライザーの使用を制限するために LSM ロジックを変更しました + + + コミットの全リストは次のとおりです(翻訳なし): + + - terminal: safely allocate pts devices from inside the container + - macro: define TIOCGPTPEER if missing + - conf: use openat() instead of open_tree() + - seccomp: don't close the mainloop, simply remove the handler + - seccomp: add seccomp_notify_fd_active api extension + - seccomp: send notify fd as part of the message + - api-extension: add missing seccomp_proxy_send_notify_fd extension + - Revert "templates/lxc-download.in: use GPG option --receive-keys instead of --recv-keys" + - lxc-download: Fix retry loop + - syscalls: add openat2() + - utils: add safe_mount_beneath() based on openat2() + - conf: switch mount_autodev() to new safe_mount_beneath() helper + - cgfsng: use safe_mount_beneath() + - utils: introduce safe_mount_beneath_at() + - conf: stash file descriptor to root mountpoint in struct lxc_rootfs + - conf: make use of stashed container mountpoint fd in mount_autodev() + - file_utils: add exists_dir_at() + - conf: harden lxc_fill_autodev() via save_mount_beneath_at() + - conf: move /dev setup to be file descriptor based + - terminal: harden terminal allocation + - lsm: rework lsm handling + - lsm: use atomic in ase we're used multi-threaded + - lsm: remove the need for atomic operations + - Updated documentation to reflect lack of support for pure cgroupv2 + - cgfsng: fix cgroup attach cgroup creation + - remove deprecated options in lxc.service fixes #3527 + - Check only rootfs as filesystem type + - cgroups: fix armhf builds + - remove useless parameters + - avoid a NULL pointer dereference in lxc-attach + - terminal: introduce lxc_terminal_signal_sigmask_safe_blocked() + - attach: use lxc_terminal_signal_sigmask_safe_blocked() + - commands: don't fail if unfreeze fails + - lxc-usernsexec: setgroups() similar to other places shouldn't fail on EPERM + - Remove obsolete setting regarding the Standard Output + - seccomp: Check if syscall is supported on compat architecture. + - seccomp: log invalid seccomp notify ids + - seccomp: improve default notification sending + - seccomp: fix compilation on powerpc + - sync: switch to new error helpers + - sync: log synchronization states + - start: improve devpts fd sending + - conf: always send response to parent waiting for devptfs_fd + - conf: account for early return when sending devpts fd + + ### サポートとアップグレード + + LXC 4.0 ブランチは 2025 年 6 月までサポートされます。 + stable のバグフィックスリリースでは、バグとセキュリティに関する問題に対する修正のみが行われますので、常に安全です。最新のバグフィックスリリースの状態を維持し、実行することをおすすめします。 + + ### ダウンロード + + - リリース tarball : [lxc-4.0.5.tar.gz](https://linuxcontainers.org/downloads/lxc/lxc-4.0.5.tar.gz) + - GPG シグネチャー : [lxc-4.0.5.tar.gz.asc](https://linuxcontainers.org/downloads/lxc/lxc-4.0.5.tar.gz.asc) From lxc-bot at linuxcontainers.org Fri Oct 30 11:02:29 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Fri, 30 Oct 2020 04:02:29 -0700 (PDT) Subject: [lxc-devel] [lxd/master] virtio-fs fixes Message-ID: <5f9bf2c5.1c69fb81.ff519.f49dSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 6d6aa4316fab5a127073cbbdfb9a807aa7007853 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 30 Oct 2020 11:02:37 +0100 Subject: [PATCH 1/3] lxd/instance/drivers: Issue warning if virtiofsd is missing Issue a warning instead of failing, if virtiofsd is missing. Signed-off-by: Thomas Hipp --- lxd/instance/drivers/driver_qemu.go | 74 +++++++++++++++-------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 8b59e1ea5b..17dfa3c246 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -884,37 +884,37 @@ func (vm *qemu) Start(stateful bool) error { } } - if cmd == "" { - return fmt.Errorf("Required binary 'virtiofsd' couldn't be found") - } + if cmd != "" { + // Start the virtiofsd process in non-daemon mode. + proc, err := subprocess.NewProcess(cmd, []string{fmt.Sprintf("--socket-path=%s", sockPath), "-o", fmt.Sprintf("source=%s", filepath.Join(vm.Path(), "config"))}, "", "") + if err != nil { + return err + } - // Start the virtiofsd process in non-daemon mode. - proc, err := subprocess.NewProcess(cmd, []string{fmt.Sprintf("--socket-path=%s", sockPath), "-o", fmt.Sprintf("source=%s", filepath.Join(vm.Path(), "config"))}, "", "") - if err != nil { - return err - } + err = proc.Start() + if err != nil { + return err + } - err = proc.Start() - if err != nil { - return err - } + revert.Add(func() { proc.Stop() }) - revert.Add(func() { proc.Stop() }) + pidPath := filepath.Join(vm.LogPath(), "virtiofsd.pid") - pidPath := filepath.Join(vm.LogPath(), "virtiofsd.pid") + err = proc.Save(pidPath) + if err != nil { + return err + } - err = proc.Save(pidPath) - if err != nil { - return err - } + // Wait for socket file to exist + for i := 0; i < 10; i++ { + if shared.PathExists(sockPath) { + break + } - // Wait for socket file to exist - for i := 0; i < 10; i++ { - if shared.PathExists(sockPath) { - break + time.Sleep(50 * time.Millisecond) } - - time.Sleep(50 * time.Millisecond) + } else { + logger.Warn("Unable to use virtio-fs for config drive, using 9p as a fallback: virtiofsd missing") } // Setup background process. @@ -1898,18 +1898,22 @@ func (vm *qemu) generateQemuConfigFile(busName string, devConfs []*deviceConfig. return "", err } - devBus, devAddr, multi = bus.allocate(busFunctionGroup9p) - err = qemuDriveConfig.Execute(sb, map[string]interface{}{ - "bus": bus.name, - "devBus": devBus, - "devAddr": devAddr, - "multifunction": multi, - "protocol": "virtio-fs", + sockPath := filepath.Join(vm.LogPath(), "virtio-fs.config.sock") - "path": filepath.Join(vm.LogPath(), "virtio-fs.config.sock"), - }) - if err != nil { - return "", err + if shared.PathExists(sockPath) { + devBus, devAddr, multi = bus.allocate(busFunctionGroup9p) + err = qemuDriveConfig.Execute(sb, map[string]interface{}{ + "bus": bus.name, + "devBus": devBus, + "devAddr": devAddr, + "multifunction": multi, + "protocol": "virtio-fs", + + "path": sockPath, + }) + if err != nil { + return "", err + } } devBus, devAddr, multi = bus.allocate(busFunctionGroupNone) From 5254f369911c7d12042e070ece5ce3c1104c13b3 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 30 Oct 2020 11:03:04 +0100 Subject: [PATCH 2/3] lxd/device: Issue warning if virtiofsd is missing Issue a warning instead of failing, if virtiofsd is missing. Signed-off-by: Thomas Hipp --- lxd/device/disk.go | 48 +++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lxd/device/disk.go b/lxd/device/disk.go index b20ac4a2ed..92e9c6b598 100644 --- a/lxd/device/disk.go +++ b/lxd/device/disk.go @@ -573,37 +573,37 @@ func (d *disk) startVM() (*deviceConfig.RunConfig, error) { } } - if cmd == "" { - return nil, fmt.Errorf("Required binary 'virtiofsd' couldn't be found") - } + if cmd != "" { + // Start the virtiofsd process in non-daemon mode. + proc, err := subprocess.NewProcess(cmd, []string{fmt.Sprintf("--socket-path=%s", sockPath), "-o", fmt.Sprintf("source=%s", srcPath)}, logPath, logPath) + if err != nil { + return nil, err + } - // Start the virtiofsd process in non-daemon mode. - proc, err := subprocess.NewProcess(cmd, []string{fmt.Sprintf("--socket-path=%s", sockPath), "-o", fmt.Sprintf("source=%s", srcPath)}, logPath, logPath) - if err != nil { - return nil, err - } + err = proc.Start() + if err != nil { + return nil, errors.Wrapf(err, "Failed to start virtiofsd for device %q", d.name) + } - err = proc.Start() - if err != nil { - return nil, errors.Wrapf(err, "Failed to start virtiofsd for device %q", d.name) - } + revert.Add(func() { proc.Stop() }) - revert.Add(func() { proc.Stop() }) + pidPath := filepath.Join(d.inst.DevicesPath(), fmt.Sprintf("virtio-fs.%s.pid", d.name)) - pidPath := filepath.Join(d.inst.DevicesPath(), fmt.Sprintf("virtio-fs.%s.pid", d.name)) + err = proc.Save(pidPath) + if err != nil { + return nil, errors.Wrapf(err, "Failed to save virtiofsd state for device %q", d.name) + } - err = proc.Save(pidPath) - if err != nil { - return nil, errors.Wrapf(err, "Failed to save virtiofsd state for device %q", d.name) - } + // Wait for socket file to exist + for i := 0; i < 10; i++ { + if shared.PathExists(sockPath) { + break + } - // Wait for socket file to exist - for i := 0; i < 10; i++ { - if shared.PathExists(sockPath) { - break + time.Sleep(50 * time.Millisecond) } - - time.Sleep(50 * time.Millisecond) + } else { + logger.Warnf("Unable to use virtio-fs for device %q, using 9p as a fallback: virtiofsd missing", d.name) } } From 0281f7f66e460c5ad93c228e8fc2ec3910bf93ab Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 30 Oct 2020 12:00:37 +0100 Subject: [PATCH 3/3] lxd/instance/drivers: Fix lxd-agent systemd unit conditions This fixes the lxd-agent mount service files, so that the 9p fallback actually works. Signed-off-by: Thomas Hipp --- lxd/instance/drivers/driver_qemu.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 17dfa3c246..cbe3770793 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -1465,7 +1465,7 @@ Documentation=https://linuxcontainers.org/lxd ConditionPathExists=/dev/virtio-ports/org.linuxcontainers.lxd After=local-fs.target lxd-agent-virtiofs.service DefaultDependencies=no -ConditionPathExists=!/run/lxd_config/drive +ConditionPathIsMountPoint=!/run/lxd_config/drive [Service] Type=oneshot @@ -1491,7 +1491,7 @@ ConditionPathExists=/dev/virtio-ports/org.linuxcontainers.lxd After=local-fs.target Before=lxd-agent-9p.service DefaultDependencies=no -ConditionPathExists=!/run/lxd_config/drive +ConditionPathIsMountPoint=!/run/lxd_config/drive [Service] Type=oneshot From lxc-bot at linuxcontainers.org Fri Oct 30 11:53:07 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Fri, 30 Oct 2020 04:53:07 -0700 (PDT) Subject: [lxc-devel] [lxd/stable-4.0] lxd/cluster/connect: Adds workaround for remote storage pool volumes using old schema in ConnectIfVolumeIsRemote Message-ID: <5f9bfea3.1c69fb81.2d33e.7e0bSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 361 bytes Desc: not available URL: -------------- next part -------------- From 92bee7be07b765539ef3db22ca9b870c405ec23f Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 30 Oct 2020 11:51:40 +0000 Subject: [PATCH] lxd/cluster/connect: Adds workaround for remote storage pool volumes using old schema in ConnectIfVolumeIsRemote Signed-off-by: Thomas Parrott --- lxd/cluster/connect.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lxd/cluster/connect.go b/lxd/cluster/connect.go index 335341b022..8d81a11011 100644 --- a/lxd/cluster/connect.go +++ b/lxd/cluster/connect.go @@ -137,6 +137,23 @@ func ConnectIfVolumeIsRemote(s *state.State, poolName string, projectName string return err } + addressesCount := len(addresses) + if addressesCount > 1 { + clusterMemberCount, err := tx.GetNodesCount() + if err != nil { + return err + } + + // Earlier schema versions created a volume DB record associated to each node in the + // cluster for remote storage pools, so if the address count equals the cluster member + // count then we take this to mean that the volume doesn't have an explicit cluster member + // and is therefore equivalent to db.ErrNoClusterMember that is used in newer schemas where + // a single remote volume DB record is created that is not associated to any single member. + if addressesCount == clusterMemberCount { + return db.ErrNoClusterMember + } + } + return nil }) if err != nil && err != db.ErrNoClusterMember { From lxc-bot at linuxcontainers.org Fri Oct 30 14:03:34 2020 From: lxc-bot at linuxcontainers.org (monstermunchkin on Github) Date: Fri, 30 Oct 2020 07:03:34 -0700 (PDT) Subject: [lxc-devel] [distrobuilder/master] generators: Add virtio-fs to lxd-agent init scripts Message-ID: <5f9c1d36.1c69fb81.3db4a.9877SMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 310 bytes Desc: not available URL: -------------- next part -------------- From 76a4b2fd347afe795df3263f2ee0fd188495b665 Mon Sep 17 00:00:00 2001 From: Thomas Hipp Date: Fri, 30 Oct 2020 15:00:46 +0100 Subject: [PATCH] generators: Add virtio-fs to lxd-agent init scripts Signed-off-by: Thomas Hipp --- generators/lxd-agent.go | 122 +++++++++++++++++++++++++++++++++++----- 1 file changed, 107 insertions(+), 15 deletions(-) diff --git a/generators/lxd-agent.go b/generators/lxd-agent.go index 88f1e3e..afa49bc 100644 --- a/generators/lxd-agent.go +++ b/generators/lxd-agent.go @@ -66,15 +66,18 @@ func (g LXDAgentGenerator) handleSystemd(sourceDir string) error { Description=LXD - agent Documentation=https://linuxcontainers.org/lxd ConditionPathExists=/dev/virtio-ports/org.linuxcontainers.lxd -Requires=lxd-agent-9p.service +Wants=lxd-agent-virtiofs.service +After=lxd-agent-virtiofs.service +Wants=lxd-agent-9p.service After=lxd-agent-9p.service + Before=cloud-init.target cloud-init.service cloud-init-local.service DefaultDependencies=no [Service] Type=notify -WorkingDirectory=/run/lxd_config/9p -ExecStart=/run/lxd_config/9p/lxd-agent +WorkingDirectory=/run/lxd_config/drive +ExecStart=/run/lxd_config/drive/lxd-agent Restart=on-failure RestartSec=5s StartLimitInterval=60 @@ -103,16 +106,17 @@ WantedBy=multi-user.target Description=LXD - agent - 9p mount Documentation=https://linuxcontainers.org/lxd ConditionPathExists=/dev/virtio-ports/org.linuxcontainers.lxd -After=local-fs.target +After=local-fs.target lxd-agent-virtiofs.service DefaultDependencies=no +ConditionPathIsMountPoint=!/run/lxd_config/drive [Service] Type=oneshot RemainAfterExit=yes ExecStartPre=-/sbin/modprobe 9pnet_virtio -ExecStartPre=/bin/mkdir -p /run/lxd_config/9p +ExecStartPre=/bin/mkdir -p /run/lxd_config/drive ExecStartPre=/bin/chmod 0700 /run/lxd_config/ -ExecStart=/bin/mount -t 9p config /run/lxd_config/9p -o access=0,trans=virtio +ExecStart=/bin/mount -t 9p config /run/lxd_config/drive -o access=0,trans=virtio [Install] WantedBy=multi-user.target @@ -128,6 +132,36 @@ WantedBy=multi-user.target return err } + lxdConfigShareMountVirtioFSUnit := `[Unit] +Description=LXD - agent - virtio-fs mount +Documentation=https://linuxcontainers.org/lxd +ConditionPathExists=/dev/virtio-ports/org.linuxcontainers.lxd +After=local-fs.target +Before=lxd-agent-9p.service +DefaultDependencies=no +ConditionPathIsMountPoint=!/run/lxd_config/drive + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStartPre=/bin/mkdir -p /run/lxd_config/drive +ExecStartPre=/bin/chmod 0700 /run/lxd_config/ +ExecStart=/bin/mount -t virtiofs config /run/lxd_config/drive + +[Install] +WantedBy=multi-user.target +` + + err = ioutil.WriteFile(filepath.Join(sourceDir, systemdPath, "lxd-agent-virtiofs.service"), []byte(lxdConfigShareMountVirtioFSUnit), 0644) + if err != nil { + return err + } + + err = os.Symlink(filepath.Join(sourceDir, systemdPath, "lxd-agent-virtiofs.service"), filepath.Join(sourceDir, "/etc/systemd/system/multi-user.target.wants/lxd-agent-virtiofs.service")) + if err != nil { + return err + } + udevPath := filepath.Join("/", "lib", "udev", "rules.d") stat, err := os.Lstat(filepath.Join(sourceDir, "lib", "udev")) @@ -148,14 +182,16 @@ func (g LXDAgentGenerator) handleOpenRC(sourceDir string) error { lxdAgentScript := `#!/sbin/openrc-run description="LXD - agent" -command=/run/lxd_config/9p/lxd-agent +command=/run/lxd_config/drive/lxd-agent command_background=true pidfile=/run/lxd-agent.pid -start_stop_daemon_args="--chdir /run/lxd_config/9p" -required_dirs=/run/lxd_config/9p +start_stop_daemon_args="--chdir /run/lxd_config/drive" +required_dirs=/run/lxd_config/drive depend() { - need lxd-agent-9p + want lxd-agent-virtiofs + after lxd-agent-virtiofs + want lxd-agent-9p after lxd-agent-9p before cloud-init before cloud-init-local @@ -176,13 +212,15 @@ depend() { description="LXD - agent - 9p mount" command=/bin/mount -command_args="-t 9p config /run/lxd_config/9p -o access=0,trans=virtio" +command_args="-t 9p config /run/lxd_config/drive -o access=0,trans=virtio" required_files=/dev/virtio-ports/org.linuxcontainers.lxd start_pre() { /sbin/modprobe 9pnet_virtio || true + # Don't proceed if the config drive is mounted already + mount | grep -q /run/lxd_config/drive && false checkpath -d /run/lxd_config -m 0700 - checkpath -d /run/lxd_config/9p + checkpath -d /run/lxd_config/drive } ` @@ -196,6 +234,31 @@ start_pre() { return err } + lxdConfigShareMountVirtioFSScript := `#!/sbin/openrc-run + + description="LXD - agent - virtio-fs mount" + command=/bin/mount + command_args="-t virtiofs config /run/lxd_config/drive" + required_files=/dev/virtio-ports/org.linuxcontainers.lxd + + start_pre() { + # Don't proceed if the config drive is mounted already + mount | grep -q /run/lxd_config/drive && false + checkpath -d /run/lxd_config -m 0700 + checkpath -d /run/lxd_config/drive + } + ` + + err = ioutil.WriteFile(filepath.Join(sourceDir, "/etc/init.d/lxd-agent-virtiofs"), []byte(lxdConfigShareMountVirtioFSScript), 0755) + if err != nil { + return err + } + + err = os.Symlink("/etc/init.d/lxd-agent-virtiofs", filepath.Join(sourceDir, "/etc/runlevels/default/lxd-agent-virtiofs")) + if err != nil { + return err + } + return nil } @@ -218,21 +281,26 @@ exec lxd-agent lxdConfigShareMountScript := `Description "LXD agent 9p mount" -start on runlevel filesystem +start on stopped lxd-agent-virtiofs pre-start script + if mount | grep -q /run/lxd_config/drive; then + stop + exit 0 + fi + if ! modprobe 9pnet_virtio; then stop exit 0 fi - mkdir -p /run/lxd_config/9p + mkdir -p /run/lxd_config/drive chmod 0700 /run/lxd_config end script task -exec mount -t 9p config /run/lxd_config/9p -o access=0,trans=virtio +exec mount -t 9p config /run/lxd_config/drive -o access=0,trans=virtio ` err = ioutil.WriteFile(filepath.Join(sourceDir, "/etc/init/lxd-agent-9p"), []byte(lxdConfigShareMountScript), 0755) @@ -240,6 +308,30 @@ exec mount -t 9p config /run/lxd_config/9p -o access=0,trans=virtio return err } + lxdConfigShareMountVirtioFSScript := `Description "LXD agent virtio-fs mount" + +start on runlevel filesystem + +pre-start script + if mount | grep -q /run/lxd_config/drive; then + stop + exit 0 + fi + + mkdir -p /run/lxd_config/drive + chmod 0700 /run/lxd_config +end script + +task + +exec mount -t virtiofs config /run/lxd_config/drive +` + + err = ioutil.WriteFile(filepath.Join(sourceDir, "/etc/init/lxd-agent-virtiofs"), []byte(lxdConfigShareMountVirtioFSScript), 0755) + if err != nil { + return err + } + return nil } From lxc-bot at linuxcontainers.org Fri Oct 30 17:53:39 2020 From: lxc-bot at linuxcontainers.org (tomponline on Github) Date: Fri, 30 Oct 2020 10:53:39 -0700 (PDT) Subject: [lxc-devel] [lxc-ci/master] bin/test-lxd-cluster: Adds cluster fan networking tests Message-ID: <5f9c5323.1c69fb81.9ec18.be1aSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 363 bytes Desc: not available URL: -------------- next part -------------- From c202714c45dc8752775474c47b6001c6b53c2d90 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 30 Oct 2020 17:52:18 +0000 Subject: [PATCH] bin/test-lxd-cluster: Adds cluster fan networking tests Signed-off-by: Thomas Parrott --- bin/test-lxd-cluster | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/bin/test-lxd-cluster b/bin/test-lxd-cluster index f1cc6a4..297a6d3 100755 --- a/bin/test-lxd-cluster +++ b/bin/test-lxd-cluster @@ -61,6 +61,11 @@ for i in $(seq "$1"); do lxc exec "${PREFIX}-$i" -- lxc config set cluster.https_address "${CLUSTER_IP}:8443" lxc exec "${PREFIX}-$i" -- lxc config set core.trust_password "cluster" lxc exec "${PREFIX}-$i" -- lxc cluster enable "${PREFIX}-$i" + lxc exec "${PREFIX}-$i" -- lxc network create lxdfan0 bridge.mode=fan + lxc exec "${PREFIX}-$i" -- lxc storage create default dir + lxc exec "${PREFIX}-$i" -- lxc profile device add default root disk path=/ pool=default + lxc exec "${PREFIX}-$i" -- lxc profile device add default eth0 nic name=eth0 network=lxdfan0 + lxc exec "${PREFIX}-$i" -- lxc network show lxdfan0 CLUSTER_CRT=$(lxc file pull "${PREFIX}-$i"/var/snap/lxd/common/lxd/cluster.crt - | sed ':a;N;$!ba;s/\n/\n\n/g') else MEMBER_IP=$(lxc exec "${PREFIX}-$i" -- ip -4 addr show dev eth0 scope global | grep inet | cut -d' ' -f6 | cut -d/ -f1) @@ -85,6 +90,20 @@ echo "==> Validating the cluster" lxc exec "${PREFIX}-1" -- lxc info lxc exec "${PREFIX}-1" -- lxc cluster list +# Test fan networking (intra fan from container and host, as well as external NAT comms) +echo "==> Test fan networking" +lxc exec "${PREFIX}-1" -- lxc launch images:ubuntu/focal u1 +lxc exec "${PREFIX}-1" -- lxc launch images:ubuntu/focal u2 + +echo "==> Wait for addresses" +sleep 10 +lxc exec "${PREFIX}-1" -- lxc list + +U2_IPV4="$(lxc exec ${PREFIX}-1 -- lxc list u2 -c4 --format=csv | cut -d' ' -f1)" +lxc exec "${PREFIX}-1" -- lxc exec u1 -- ping -c1 -4 linuxcontainers.org +lxc exec "${PREFIX}-1" -- lxc exec u1 -- ping -c1 "${U2_IPV4}" +lxc exec "${PREFIX}-1" -- ping -c1 "${U2_IPV4}" + # Upgrade the cluster echo "==> Upgrading the cluster" for i in $(seq "$1"); do From lxc-bot at linuxcontainers.org Fri Oct 30 21:46:05 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Fri, 30 Oct 2020 14:46:05 -0700 (PDT) Subject: [lxc-devel] [lxd/master] lxd/device/sriov: Harden calls to ip link vf Message-ID: <5f9c899d.1c69fb81.3e1a1.379bSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 588 bytes Desc: not available URL: -------------- next part -------------- From 55a9969fa48d094bcafdea04dc0a5adee3532eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 30 Oct 2020 15:57:51 -0400 Subject: [PATCH] lxd/device/sriov: Harden calls to ip link vf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We've seen some systems where the driver is in an inconsistent state briefly and returns EBUSY, effectively needing a retry. So let's just assume that this may happen on any VF reconfig and wrap everything with TryRunCommand. Signed-off-by: Stéphane Graber --- lxd/device/nic_sriov.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lxd/device/nic_sriov.go b/lxd/device/nic_sriov.go index 64aa66e66e..fe358a3252 100644 --- a/lxd/device/nic_sriov.go +++ b/lxd/device/nic_sriov.go @@ -433,7 +433,7 @@ func (d *nicSRIOV) setupSriovParent(vfDevice string, vfID int, volatile map[stri // Setup VF VLAN if specified. if d.config["vlan"] != "" { - _, err := shared.RunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "vlan", d.config["vlan"]) + _, err := shared.TryRunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "vlan", d.config["vlan"]) if err != nil { return vfPCIDev, err } @@ -450,25 +450,25 @@ func (d *nicSRIOV) setupSriovParent(vfDevice string, vfID int, volatile map[stri } // Set MAC on VF (this combined with spoof checking prevents any other MAC being used). - _, err = shared.RunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "mac", mac) + _, err = shared.TryRunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "mac", mac) if err != nil { return vfPCIDev, err } // Now that MAC is set on VF, we can enable spoof checking. - _, err = shared.RunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "spoofchk", "on") + _, err = shared.TryRunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "spoofchk", "on") if err != nil { return vfPCIDev, err } } else { // Reset VF to ensure no previous MAC restriction exists. - _, err := shared.RunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "mac", "00:00:00:00:00:00") + _, err := shared.TryRunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "mac", "00:00:00:00:00:00") if err != nil { return vfPCIDev, err } // Ensure spoof checking is disabled if not enabled in instance. - _, err = shared.RunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "spoofchk", "off") + _, err = shared.TryRunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "spoofchk", "off") if err != nil { return vfPCIDev, err } @@ -481,7 +481,7 @@ func (d *nicSRIOV) setupSriovParent(vfDevice string, vfID int, volatile map[stri mac = volatile["last_state.hwaddr"] } - _, err = shared.RunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "mac", mac) + _, err = shared.TryRunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "mac", mac) if err != nil { return vfPCIDev, err } @@ -712,7 +712,7 @@ func (d *nicSRIOV) restoreSriovParent(volatile map[string]string) error { // Reset VF VLAN if specified if volatile["last_state.vf.vlan"] != "" { - _, err := shared.RunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "vlan", volatile["last_state.vf.vlan"]) + _, err := shared.TryRunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "vlan", volatile["last_state.vf.vlan"]) if err != nil { return err } @@ -726,7 +726,7 @@ func (d *nicSRIOV) restoreSriovParent(volatile map[string]string) error { mode = "on" } - _, err := shared.RunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "spoofchk", mode) + _, err := shared.TryRunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "spoofchk", mode) if err != nil { return err } @@ -734,7 +734,7 @@ func (d *nicSRIOV) restoreSriovParent(volatile map[string]string) error { // Reset VF MAC specified if specified. if volatile["last_state.vf.hwaddr"] != "" { - _, err := shared.RunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "mac", volatile["last_state.vf.hwaddr"]) + _, err := shared.TryRunCommand("ip", "link", "set", "dev", d.config["parent"], "vf", volatile["last_state.vf.id"], "mac", volatile["last_state.vf.hwaddr"]) if err != nil { return err } From lxc-bot at linuxcontainers.org Sat Oct 31 01:16:56 2020 From: lxc-bot at linuxcontainers.org (stgraber on Github) Date: Fri, 30 Oct 2020 18:16:56 -0700 (PDT) Subject: [lxc-devel] [lxd/master] Add `rebase` mode for `zfs.clone_copy` Message-ID: <5f9cbb08.1c69fb81.4416a.4e5aSMTPIN_ADDED_MISSING@mx.google.com> A non-text attachment was scrubbed... Name: not available Type: text/x-mailbox Size: 301 bytes Desc: not available URL: -------------- next part -------------- From 3f965e407fc42df23a43432721d24b106cd10d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 30 Oct 2020 18:35:16 -0400 Subject: [PATCH 1/4] api: Add storage_zfs_clone_copy_rebase extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- doc/api-extensions.md | 5 +++++ shared/version/api.go | 1 + 2 files changed, 6 insertions(+) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 1742003087..555e83d8c6 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -1217,3 +1217,8 @@ both at the network and NIC level. ## tpm\_device\_type This introduces the `tpm` device type. + +## storage\_zfs\_clone\_copy\_rebase +This introduces `rebase` as a value for zfs.clone\_copy causing LXD to +track down any "image" dataset in the ancestry line and then perform +send/receive on top of that. diff --git a/shared/version/api.go b/shared/version/api.go index 2ff1e77db4..2c986fa5f0 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -234,6 +234,7 @@ var APIExtensions = []string{ "network_ovn_nat", "network_ovn_external_routes_remove", "tpm_device_type", + "storage_zfs_clone_copy_rebase", } // APIExtensionsCount returns the number of available API extensions. From a12c58c769865dba839680d63def728dcfc74991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 30 Oct 2020 18:35:34 -0400 Subject: [PATCH 2/4] doc/storage: Allow 'rebase' in zfs.clone_copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- doc/storage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/storage.md b/doc/storage.md index 67dc5786f1..5d95e3808e 100644 --- a/doc/storage.md +++ b/doc/storage.md @@ -35,7 +35,7 @@ volume.block.mount\_options | string | block based driver (lvm) volume.size | string | appropriate driver | unlimited (10GB for block) | storage | Default volume size volume.zfs.remove\_snapshots | bool | zfs driver | false | storage | Remove snapshots as needed volume.zfs.use\_refquota | bool | zfs driver | false | storage | Use refquota instead of quota for space. -zfs.clone\_copy | bool | zfs driver | true | storage\_zfs\_clone\_copy | Whether to use ZFS lightweight clones rather than full dataset copies. +zfs.clone\_copy | string | zfs driver | true | storage\_zfs\_clone\_copy | Whether to use ZFS lightweight clones rather than full dataset copies (boolean) or "rebase" to copy based on the initial image. zfs.pool\_name | string | zfs driver | name of the pool | storage | Name of the zpool Storage pool configuration keys can be set using the lxc tool with: From ca89ffbcbaeeb33bf4dd5fd2562aba77fe2aac2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 30 Oct 2020 18:42:22 -0400 Subject: [PATCH 3/4] lxd/storage: Allow 'rebase' as value for zfs.clone_copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxd/storage_pools_config.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lxd/storage_pools_config.go b/lxd/storage_pools_config.go index 5c6892bd12..ba4470d50a 100644 --- a/lxd/storage_pools_config.go +++ b/lxd/storage_pools_config.go @@ -78,7 +78,13 @@ var storagePoolConfigKeys = map[string]func(value string) error{ "volume.zfs.use_refquota": validate.Optional(validate.IsBool), // valid drivers: zfs - "zfs.clone_copy": validate.Optional(validate.IsBool), + "zfs.clone_copy": validate.Optional(func(value string) error { + if value == "rebase" { + return nil + } + + return validate.IsBool(value) + }), "zfs.pool_name": validate.IsAny, "rsync.bwlimit": validate.IsAny, From 5b2865972244ebac85d521c666cd1526c6312839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 30 Oct 2020 19:19:30 -0400 Subject: [PATCH 4/4] lxd/storage/zfs: Add support for clone_copy rebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #8096 Signed-off-by: Stéphane Graber --- lxd/storage/drivers/driver_zfs.go | 10 ++++++-- lxd/storage/drivers/driver_zfs_volumes.go | 31 ++++++++++++++++++++++- lxd/storage_pools_config.go | 4 +-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/lxd/storage/drivers/driver_zfs.go b/lxd/storage/drivers/driver_zfs.go index 956f8297fb..31c4f3e771 100644 --- a/lxd/storage/drivers/driver_zfs.go +++ b/lxd/storage/drivers/driver_zfs.go @@ -342,8 +342,14 @@ func (d *zfs) Delete(op *operations.Operation) error { // Validate checks that all provide keys are supported and that no conflicting or missing configuration is present. func (d *zfs) Validate(config map[string]string) error { rules := map[string]func(value string) error{ - "zfs.pool_name": validate.IsAny, - "zfs.clone_copy": validate.Optional(validate.IsBool), + "zfs.pool_name": validate.IsAny, + "zfs.clone_copy": validate.Optional(func(value string) error { + if value == "rebase" { + return nil + } + + return validate.IsBool(value) + }), "volume.zfs.remove_snapshots": validate.Optional(validate.IsBool), "volume.zfs.use_refquota": validate.Optional(validate.IsBool), } diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go index 811fb11450..bcf0c016d2 100644 --- a/lxd/storage/drivers/driver_zfs_volumes.go +++ b/lxd/storage/drivers/driver_zfs_volumes.go @@ -510,7 +510,36 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool if len(snapshots) > 0 { sender = exec.Command("zfs", "send", "-R", srcSnapshot) } else { - sender = exec.Command("zfs", "send", srcSnapshot) + if d.config["zfs.clone_copy"] == "rebase" { + var err error + origin := d.dataset(srcVol, false) + for { + fields := strings.SplitN(origin, "@", 2) + + // If the origin is a @readonly snapshot under a /images/ path (/images or deleted/images), we're done. + if len(fields) > 1 && strings.Contains(fields[0], "/images/") && fields[1] == "readonly" { + break + } + + origin, err = d.getDatasetProperty(origin, "origin") + if err != nil { + return err + } + + if origin == "" || origin == "-" { + origin = "" + break + } + } + + if origin != "" && origin != srcSnapshot { + sender = exec.Command("zfs", "send", "-i", origin, srcSnapshot) + } else { + sender = exec.Command("zfs", "send", srcSnapshot) + } + } else { + sender = exec.Command("zfs", "send", srcSnapshot) + } } // Configure the pipes. diff --git a/lxd/storage_pools_config.go b/lxd/storage_pools_config.go index ba4470d50a..d593655bfd 100644 --- a/lxd/storage_pools_config.go +++ b/lxd/storage_pools_config.go @@ -85,8 +85,8 @@ var storagePoolConfigKeys = map[string]func(value string) error{ return validate.IsBool(value) }), - "zfs.pool_name": validate.IsAny, - "rsync.bwlimit": validate.IsAny, + "zfs.pool_name": validate.IsAny, + "rsync.bwlimit": validate.IsAny, // valid drivers: btrfs, ceph, cephfs, zfs "rsync.compression": validate.Optional(validate.IsBool),