From e3d3aa0e4e214ec8afdc069c60f984f3f7c4e97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20Peliz=C3=A4us?= Date: Mon, 14 Oct 2024 17:48:33 +0200 Subject: [PATCH 01/31] lxd/firewall/drivers: Don't masquerade multicast traffic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case br_netfilter is loaded on the host all traffic is also passing the postrouting chain. This leads to false masquerading of multicast traffic if the traffic stays within the network. Signed-off-by: Julian Pelizäus (cherry picked from commit 4c9e8e72d32dea98f858a266fa251121ebd1c792) --- lxd/firewall/drivers/drivers_nftables_templates.go | 8 ++++++-- lxd/firewall/drivers/drivers_xtables.go | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lxd/firewall/drivers/drivers_nftables_templates.go b/lxd/firewall/drivers/drivers_nftables_templates.go index be76d4366ff1..40f6ec757212 100644 --- a/lxd/firewall/drivers/drivers_nftables_templates.go +++ b/lxd/firewall/drivers/drivers_nftables_templates.go @@ -32,9 +32,13 @@ chain pstrt{{.chainSeparator}}{{.networkName}} { {{- range $ipFamily, $config := .rules}} {{if $config.SNATAddress -}} - {{$ipFamily}} saddr {{$config.Subnet}} {{$ipFamily}} daddr != {{$config.Subnet}} snat {{$config.SNATAddress}} + # If the output interface name is the network itself the traffic stays within the network. + # It's important to check for both the destination address and the output interface + # to not falsely snat/masquerade multicast traffic whose destination address it outside of the subnet. + # In case br_netfilter is loaded on the host multicast traffic also traverses the postrouting chain. + {{$ipFamily}} saddr {{$config.Subnet}} {{$ipFamily}} daddr != {{$config.Subnet}} oifname != {{$.networkName}} snat {{$config.SNATAddress}} {{else -}} - {{$ipFamily}} saddr {{$config.Subnet}} {{$ipFamily}} daddr != {{$config.Subnet}} masquerade + {{$ipFamily}} saddr {{$config.Subnet}} {{$ipFamily}} daddr != {{$config.Subnet}} oifname != {{$.networkName}} masquerade {{- end}} {{- end}} } diff --git a/lxd/firewall/drivers/drivers_xtables.go b/lxd/firewall/drivers/drivers_xtables.go index 13c534a5def4..c3eae0a15e3f 100644 --- a/lxd/firewall/drivers/drivers_xtables.go +++ b/lxd/firewall/drivers/drivers_xtables.go @@ -352,6 +352,11 @@ func (d Xtables) networkSetupOutboundNAT(networkName string, subnet *net.IPNet, args := []string{ "-s", subnet.String(), "!", "-d", subnet.String(), + // If the output interface name is the network itself the traffic stays within the network. + // It's important to check for both the destination address and the output interface + // to not falsely snat/masquerade multicast traffic whose destination address it outside of the subnet. + // In case br_netfilter is loaded on the host multicast traffic also traverses the postrouting chain. + "!", "-o", networkName, } // If SNAT IP not supplied then use the IP of the outbound interface (MASQUERADE). From 08c0c244cca9777d988a85690785a80bee96e403 Mon Sep 17 00:00:00 2001 From: Mark Bolton Date: Wed, 9 Oct 2024 20:58:27 -0700 Subject: [PATCH 02/31] lxd/network: Support VLAN tagging for OVN uplinks with native bridge parents Signed-off-by: Mark Bolton (cherry picked from commit 920700be6403f89a1fcd793b9f355d4845c77fc3) --- lxd/network/driver_ovn.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 4c0010fa7605..c3615bccc4ab 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1531,6 +1531,14 @@ func (n *ovn) startUplinkPortBridgeNative(uplinkNet Network, bridgeDevice string return fmt.Errorf("Failed to bring up uplink veth interface %q: %w", vars.uplinkEnd, err) } + // Add VLAN filter entry to the uplink end of the veth interface. + if uplinkNetConfig["vlan"] != "" { + err = link.BridgeVLANAdd(uplinkNetConfig["vlan"], true, true, false) + if err != nil { + return fmt.Errorf("Failed to configure VLAN for uplink veth interface %q: %w", vars.uplinkEnd, err) + } + } + // Ensure uplink OVS end veth interface is up. link = &ip.Link{Name: vars.ovsEnd} err = link.SetUp() @@ -1646,6 +1654,11 @@ func (n *ovn) startUplinkPortPhysical(uplinkNet Network) error { return n.startUplinkPortBridgeNative(uplinkNet, uplinkHostName) } + // Handle case where uplink interface is bridge and VLAN is specified. + if IsNativeBridge(uplinkConfig["parent"]) && uplinkConfig["vlan"] != "" { + return n.startUplinkPortBridgeNative(uplinkNet, uplinkConfig["parent"]) + } + // Detect if uplink interface is a OVS bridge. ovs := openvswitch.NewOVS() isOVSBridge, _ := ovs.BridgeExists(uplinkHostName) @@ -1853,7 +1866,7 @@ func (n *ovn) deleteUplinkPortPhysical(uplinkNet Network) error { uplinkHostName := GetHostDevice(uplinkConfig["parent"], uplinkConfig["vlan"]) // Detect if uplink interface is a native bridge. - if IsNativeBridge(uplinkHostName) { + if IsNativeBridge(uplinkHostName) || (IsNativeBridge(uplinkConfig["parent"]) && uplinkConfig["vlan"] != "") { return n.deleteUplinkPortBridgeNative(uplinkNet) } From 3fa8f4e5e6d42b110c61a7a0f9bc8ca987a2e798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 1 Oct 2024 17:07:04 -0400 Subject: [PATCH 03/31] lxd/network/driver_ovn: Fix CIDR size check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber (cherry picked from commit https://github.com/lxc/incus/pull/1271/commits/9426885a9e2346797e01c0ad7ff06a2fb2ea7548) Signed-off-by: Alexander Mikhalitsyn License: Apache-2.0 (cherry picked from commit 2bf31d4531f2c32c64b10f30f6440ae0877ecfb9) --- 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 c3615bccc4ab..dd4d8daf5ada 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -601,7 +601,7 @@ func (n *ovn) Validate(config map[string]string) error { _, ipv6Net, _ := net.ParseCIDR(config["ipv6.address"]) if ipv6Net != nil { ones, _ := ipv6Net.Mask.Size() - if ones < 64 { + if ones > 64 { return fmt.Errorf("IPv6 subnet must be at least a /64") } } From 40d6e8189d159c8f1ecb1d17cf089c78bb1c4ae7 Mon Sep 17 00:00:00 2001 From: Alexander Mikhalitsyn Date: Tue, 15 Oct 2024 12:27:11 +0200 Subject: [PATCH 04/31] lxd/network/driver_bridge: fix IPv6 CIDR size check Signed-off-by: Alexander Mikhalitsyn (cherry picked from commit f56f357f347afe2c48673c5291f35089aa5d5b84) --- 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 9716ab3e17d1..624e8a9c1353 100644 --- a/lxd/network/driver_bridge.go +++ b/lxd/network/driver_bridge.go @@ -1506,7 +1506,7 @@ func (n *bridge) setup(oldConfig map[string]string) error { subnetSize, _ := subnet.Mask.Size() - if subnetSize > 64 { + if subnetSize < 64 { n.logger.Warn("IPv6 networks with a prefix larger than 64 aren't properly supported by dnsmasq") err = n.state.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error { return tx.UpsertWarningLocalNode(ctx, n.project, entity.TypeNetwork, int(n.id), warningtype.LargerIPv6PrefixThanSupported, "") From 8603e94c6649ad8a4c2b7c79a476e15eaf3a4af2 Mon Sep 17 00:00:00 2001 From: Alexander Mikhalitsyn Date: Tue, 15 Oct 2024 12:27:59 +0200 Subject: [PATCH 05/31] lxd/network/driver_ovn: fix IPv6 CIDR size check Signed-off-by: Alexander Mikhalitsyn (cherry picked from commit d4eebe06341bdfd0c7ba34ac46ee4bf6abc5e45f) --- 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 dd4d8daf5ada..5983bb215596 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -4424,7 +4424,7 @@ func (n *ovn) DHCPv6Subnet() *net.IPNet { if subnet != nil { ones, _ := subnet.Mask.Size() - if ones < 64 { + if ones > 64 { return nil // OVN only supports DHCPv6 allocated using EUI64 (which needs at least a /64). } } From f2dd563487460bc8b306e6ce7b24dafa248c75c2 Mon Sep 17 00:00:00 2001 From: Mark Laing Date: Tue, 15 Oct 2024 14:59:57 +0100 Subject: [PATCH 06/31] shared: Change error message on ParseCert. The function does not need to called with file contents. Returning "invalid certificate file" does not make sense if called on data sent over the API. Signed-off-by: Mark Laing (cherry picked from commit 298aaadd29b9122f1d6913a62b073c4d1cfa42c3) --- shared/cert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/cert.go b/shared/cert.go index 57c272bc701d..b600c5b5cc17 100644 --- a/shared/cert.go +++ b/shared/cert.go @@ -434,7 +434,7 @@ func GenerateMemCert(client bool, options CertOptions) ([]byte, []byte, error) { func ParseCert(cert []byte) (*x509.Certificate, error) { certBlock, _ := pem.Decode(cert) if certBlock == nil { - return nil, fmt.Errorf("Invalid certificate file") + return nil, fmt.Errorf("Invalid PEM block") } return x509.ParseCertificate(certBlock.Bytes) From e360a218b03cc8d29579c21c4cf2d06265b21b1d Mon Sep 17 00:00:00 2001 From: Simon Deziel Date: Tue, 15 Oct 2024 10:57:25 -0400 Subject: [PATCH 07/31] Revert "github: move `make doc-linkcheck` to Tiobe TICS job" This reverts commit 15ad72f05365b15846a65809433bf2d246830641. Signed-off-by: Simon Deziel (cherry picked from commit 5a2cc4e9100de0061015a8fdd00c198822f2cb75) --- .github/workflows/tests.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a5be5abee3f6..6029e822ba36 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -417,13 +417,6 @@ jobs: calc: ALL tmpdir: /tmp/tics - # Verify doc links work - - name: Run link checker - shell: 'script -q -e -c "export TERM=xterm-256color; bash {0}"' - run: | - set -eux - make doc-linkcheck - client: name: Client strategy: @@ -554,9 +547,13 @@ jobs: fail-on-error: true woke-args: "*.md **/*.md -c https://github.com/canonical/Inclusive-naming/raw/main/config.yml" - # XXX: not running `make doc-linkcheck` here as it often fails due to - # remote sites applying rate limits. It is instead ran on a schedule - # along with Tiobe TICS + - name: Run link checker + # Run link checker on PRs only + if: ${{ github.event_name != 'push' }} + shell: 'script -q -e -c "export TERM=xterm-256color; bash {0}"' + run: | + set -eux + make doc-linkcheck - name: Upload documentation artifacts if: always() From 652df52ff9ad2438261bc51a99c518c3f020b92b Mon Sep 17 00:00:00 2001 From: Simon Deziel Date: Tue, 15 Oct 2024 11:01:11 -0400 Subject: [PATCH 08/31] github: move `make doc-linkcheck` back to doc Change that test step to only run on schedule as it often fails due to remote sites applying rate limits. Signed-off-by: Simon Deziel (cherry picked from commit d372fb087a09bb4dea9f6b181f2b1398993cb6a3) --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6029e822ba36..20a5a9a66c76 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -548,8 +548,8 @@ jobs: woke-args: "*.md **/*.md -c https://github.com/canonical/Inclusive-naming/raw/main/config.yml" - name: Run link checker - # Run link checker on PRs only - if: ${{ github.event_name != 'push' }} + # Run link checker during scheduled CI runs only + if: ${{ github.event_name == 'schedule' }} shell: 'script -q -e -c "export TERM=xterm-256color; bash {0}"' run: | set -eux From 30ee94825c1ad667e8aefe83a92d428dfae245e3 Mon Sep 17 00:00:00 2001 From: Simon Deziel Date: Tue, 15 Oct 2024 14:08:05 -0400 Subject: [PATCH 09/31] test/includes/lxc: silently set +x Signed-off-by: Simon Deziel (cherry picked from commit f6a4cb7639b9184a24460aeea37df50ce3efd6cc) --- test/includes/lxc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/includes/lxc.sh b/test/includes/lxc.sh index 901b82e68184..140a10c18660 100644 --- a/test/includes/lxc.sh +++ b/test/includes/lxc.sh @@ -1,12 +1,12 @@ # lxc CLI related test helpers. lxc() { - set +x + { set +x; } 2>/dev/null LXC_LOCAL=1 lxc_remote "$@" } lxc_remote() { - set +x + { set +x; } 2>/dev/null local injected cmd arg injected=0 From f97c3cce02c26a970929e65bd76803e9ce259ecd Mon Sep 17 00:00:00 2001 From: Simon Deziel Date: Tue, 15 Oct 2024 14:08:08 -0400 Subject: [PATCH 10/31] test/includes/lxd: silently set +x Signed-off-by: Simon Deziel (cherry picked from commit 3c0512ed1af0be5042c18a3fa9326c0f8b7a7955) --- test/includes/lxd.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/includes/lxd.sh b/test/includes/lxd.sh index 91f5c7ca459f..7049762dec05 100644 --- a/test/includes/lxd.sh +++ b/test/includes/lxd.sh @@ -1,7 +1,7 @@ # LXD-related test helpers. spawn_lxd() { - set +x + { set +x; } 2>/dev/null # LXD_DIR is local here because since $(lxc) is actually a function, it # overwrites the environment and we would lose LXD_DIR's value otherwise. @@ -77,7 +77,7 @@ spawn_lxd() { } respawn_lxd() { - set +x + { set +x; } 2>/dev/null # LXD_DIR is local here because since $(lxc) is actually a function, it # overwrites the environment and we would lose LXD_DIR's value otherwise. From 809821d0a4a4bdd31723e6ac504c985361d6c06d Mon Sep 17 00:00:00 2001 From: Simon Deziel Date: Tue, 15 Oct 2024 15:35:39 -0400 Subject: [PATCH 11/31] Makefile: stop testing with flake8 during static-analysis `flake8` isn't the best style checker ATM and since `import-busybox` doesn't change often, don't botter checking it all the time. `black` or `ruff format` (which applies `black`'s format) are more popular these days. Signed-off-by: Simon Deziel (cherry picked from commit 75311959873eec15fa7bf48b7834aec33881ae96) --- Makefile | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Makefile b/Makefile index 63d3f10c5b09..1be161c45039 100644 --- a/Makefile +++ b/Makefile @@ -263,11 +263,6 @@ ifneq "$(shell shellcheck --version | grep version: | cut -d ' ' -f2)" "0.8.0" @echo "WARN: shellcheck version is not 0.8.0" endif endif -ifeq ($(shell command -v flake8),) - echo "Please install flake8" - exit 1 -endif - flake8 test/deps/import-busybox shellcheck test/*.sh test/includes/*.sh test/suites/*.sh test/backends/*.sh test/lint/*.sh test/extras/*.sh NOT_EXEC="$(shell find test/lint -type f -not -executable)"; \ if [ -n "$$NOT_EXEC" ]; then \ From 7b45a8e090284d2be265471a32ddaea8d82e56e5 Mon Sep 17 00:00:00 2001 From: Simon Deziel Date: Tue, 15 Oct 2024 15:43:27 -0400 Subject: [PATCH 12/31] github: don't install flake8 Signed-off-by: Simon Deziel (cherry picked from commit 429ac8a75c1173a4cb29462f6d16fec1eff8aec1) --- .github/workflows/tests.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 20a5a9a66c76..74ae40c06992 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -76,10 +76,8 @@ jobs: - name: Install build dependencies uses: ./.github/actions/install-lxd-builddeps - - name: Install test dependencies + - name: Download minio/mc run: | - python3 -m pip install flake8 - # Download minio ready to include in dependencies for system tests. mkdir -p "$(go env GOPATH)/bin" curl -sSfL https://dl.min.io/server/minio/release/linux-amd64/minio --output "$(go env GOPATH)/bin/minio" From dd017c97ed5e3abe0b1a050268808301a76a817c Mon Sep 17 00:00:00 2001 From: Simon Deziel Date: Tue, 15 Oct 2024 15:45:10 -0400 Subject: [PATCH 13/31] test/deps/import-busybox: format with `ruff format` Signed-off-by: Simon Deziel (cherry picked from commit 85faaac1c0d3b0d59a668dd7234c7faae00a06b2) --- test/deps/import-busybox | 180 +++++++++++++++++++++------------------ 1 file changed, 98 insertions(+), 82 deletions(-) diff --git a/test/deps/import-busybox b/test/deps/import-busybox index 5a0fcfce0193..1866c047e889 100755 --- a/test/deps/import-busybox +++ b/test/deps/import-busybox @@ -17,7 +17,7 @@ import uuid class FriendlyParser(argparse.ArgumentParser): def error(self, message): - sys.stderr.write('\nerror: %s\n' % message) + sys.stderr.write("\nerror: %s\n" % message) self.print_help() sys.exit(2) @@ -25,10 +25,10 @@ class FriendlyParser(argparse.ArgumentParser): def find_on_path(command): """Is command on the executable search path?""" - if 'PATH' not in os.environ: + if "PATH" not in os.environ: return False - path = os.environ['PATH'] + path = os.environ["PATH"] for element in path.split(os.pathsep): if not element: continue @@ -41,7 +41,7 @@ def find_on_path(command): class UnixHTTPConnection(http.client.HTTPConnection): def __init__(self, path): - http.client.HTTPConnection.__init__(self, 'localhost') + http.client.HTTPConnection.__init__(self, "localhost") self.path = path def connect(self): @@ -70,8 +70,10 @@ class LXD(object): data["project"] = self.project self.lxd.request( method, - "%s?%s" % "&".join(["%s=%s" % (key, value) - for key, value in data.items()]), headers) + "%s?%s" + % "&".join(["%s=%s" % (key, value) for key, value in data.items()]), + headers, + ) else: path += "?project=%s" % self.project self.lxd.request(method, path, data, headers) @@ -81,8 +83,7 @@ class LXD(object): return r.status, d def aliases_create(self, name, target): - data = json.dumps({"target": target, - "name": name}) + data = json.dumps({"target": target, "name": name}) status, data = self.rest_call("/1.0/images/aliases", data, "POST") @@ -90,8 +91,7 @@ class LXD(object): raise Exception("Failed to create alias: %s" % name) def aliases_remove(self, name): - status, data = self.rest_call("/1.0/images/aliases/%s" % name, - method="DELETE") + status, data = self.rest_call("/1.0/images/aliases/%s" % name, method="DELETE") if status != 200: raise Exception("Failed to remove alias: %s" % name) @@ -99,28 +99,27 @@ class LXD(object): def aliases_list(self): status, data = self.rest_call("/1.0/images/aliases") - return [alias.split("/1.0/images/aliases/")[-1] - for alias in data['metadata']] + return [alias.split("/1.0/images/aliases/")[-1] for alias in data["metadata"]] def images_list(self, recursive=False): if recursive: status, data = self.rest_call("/1.0/images?recursion=1") - return data['metadata'] + return data["metadata"] else: status, data = self.rest_call("/1.0/images") - return [image.split("/1.0/images/")[-1] - for image in data['metadata']] + return [image.split("/1.0/images/")[-1] for image in data["metadata"]] def images_upload(self, path, public, filename=None): headers = {} if public: - headers['X-LXD-public'] = "1" + headers["X-LXD-public"] = "1" if isinstance(path, str): - headers['Content-Type'] = "application/octet-stream" + headers["Content-Type"] = "application/octet-stream" - status, data = self.rest_call("/1.0/images", open(path, "rb"), - "POST", headers) + status, data = self.rest_call( + "/1.0/images", open(path, "rb"), "POST", headers + ) else: meta_path, rootfs_path = path boundary = str(uuid.uuid1()) @@ -128,12 +127,15 @@ class LXD(object): upload_path = os.path.join(self.workdir, "upload") body = open(upload_path, "wb+") - for name, path in [("metadata", meta_path), - ("rootfs", rootfs_path)]: + for name, path in [("metadata", meta_path), ("rootfs", rootfs_path)]: body.write(bytes("--%s\r\n" % boundary, "utf-8")) - body.write(bytes("Content-Disposition: form-data; " - "name=%s;%s\r\n" % (name, filename_entry), - "utf-8")) + body.write( + bytes( + "Content-Disposition: form-data; " + "name=%s;%s\r\n" % (name, filename_entry), + "utf-8", + ) + ) body.write(b"Content-Type: application/octet-stream\r\n") body.write(b"\r\n") with open(path, "rb") as fd: @@ -144,26 +146,23 @@ class LXD(object): body.write(b"\r\n") body.close() - headers['Content-Type'] = "multipart/form-data; boundary=%s" \ - % boundary + headers["Content-Type"] = "multipart/form-data; boundary=%s" % boundary - status, data = self.rest_call("/1.0/images", - open(upload_path, "rb"), - "POST", headers) + status, data = self.rest_call( + "/1.0/images", open(upload_path, "rb"), "POST", headers + ) if status != 202: raise Exception("Failed to upload the image: %s" % status) - status, data = self.rest_call(data['operation'] + "/wait", - "", "GET", {}) + status, data = self.rest_call(data["operation"] + "/wait", "", "GET", {}) if status != 200: raise Exception("Failed to query the operation: %s" % status) - if data['status_code'] != 200: - raise Exception("Failed to import the image: %s" % - data['metadata']) + if data["status_code"] != 200: + raise Exception("Failed to import the image: %s" % data["metadata"]) - return data['metadata']['metadata'] + return data["metadata"]["metadata"] class BusyBox(object): @@ -185,19 +184,19 @@ class BusyBox(object): target_tarball = tarfile.open(destination_tar, "w:") if split: - destination_tar_rootfs = os.path.join(self.workdir, - "busybox.rootfs.tar") + destination_tar_rootfs = os.path.join(self.workdir, "busybox.rootfs.tar") target_tarball_rootfs = tarfile.open(destination_tar_rootfs, "w:") - metadata = {'architecture': os.uname()[4], - 'creation_date': int(os.stat("/bin/busybox").st_ctime), - 'properties': { - 'os': "BusyBox", - 'architecture': os.uname()[4], - 'description': "BusyBox %s" % os.uname()[4], - 'name': "busybox-%s" % os.uname()[4] - }, - } + metadata = { + "architecture": os.uname()[4], + "creation_date": int(os.stat("/bin/busybox").st_ctime), + "properties": { + "os": "BusyBox", + "architecture": os.uname()[4], + "description": "BusyBox %s" % os.uname()[4], + "name": "busybox-%s" % os.uname()[4], + }, + } # Add busybox with open("/bin/busybox", "rb") as fd: @@ -212,9 +211,11 @@ class BusyBox(object): target_tarball.addfile(busybox_file, fd) # Add symlinks - busybox = subprocess.Popen(["/bin/busybox", "--list-full"], - stdout=subprocess.PIPE, - universal_newlines=True) + busybox = subprocess.Popen( + ["/bin/busybox", "--list-full"], + stdout=subprocess.PIPE, + universal_newlines=True, + ) busybox.wait() for path in busybox.stdout.read().split("\n"): @@ -246,9 +247,8 @@ class BusyBox(object): # Deal with templating if template: metadata["templates"] = { - "/template": { - "when": template, - "template": "template.tpl"}} + "/template": {"when": template, "template": "template.tpl"} + } directory_file = tarfile.TarInfo() directory_file.type = tarfile.DIRTYPE @@ -267,19 +267,24 @@ user.foo: {{ config_get("user.foo", "_unset_") }} template_file = tarfile.TarInfo() template_file.size = len(template) template_file.name = "templates/template.tpl" - target_tarball.addfile(template_file, - io.BytesIO(template.encode())) + target_tarball.addfile(template_file, io.BytesIO(template.encode())) # Add the metadata file - metadata_yaml = json.dumps(metadata, sort_keys=True, - indent=4, separators=(',', ': '), - ensure_ascii=False).encode('utf-8') + b"\n" + metadata_yaml = ( + json.dumps( + metadata, + sort_keys=True, + indent=4, + separators=(",", ": "), + ensure_ascii=False, + ).encode("utf-8") + + b"\n" + ) metadata_file = tarfile.TarInfo() metadata_file.size = len(metadata_yaml) metadata_file.name = "metadata.yaml" - target_tarball.addfile(metadata_file, - io.BytesIO(metadata_yaml)) + target_tarball.addfile(metadata_file, io.BytesIO(metadata_yaml)) # Add an /etc/inittab; this is to work around: # http://lists.busybox.net/pipermail/busybox/2015-November/083618.html @@ -303,8 +308,7 @@ user.foo: {{ config_get("user.foo", "_unset_") }} if split: r = subprocess.call([xz, "-9", destination_tar_rootfs]) if r: - raise Exception("Failed to compress: %s" % - destination_tar_rootfs) + raise Exception("Failed to compress: %s" % destination_tar_rootfs) return destination_tar + ".xz", destination_tar_rootfs + ".xz" else: return destination_tar + ".xz" @@ -312,7 +316,7 @@ user.foo: {{ config_get("user.foo", "_unset_") }} if __name__ == "__main__": if "LXD_DIR" in os.environ: - lxd_socket = os.path.join(os.environ['LXD_DIR'], "unix.socket") + lxd_socket = os.path.join(os.environ["LXD_DIR"], "unix.socket") else: lxd_socket = "/var/lib/lxd/unix.socket" @@ -334,23 +338,25 @@ if __name__ == "__main__": if args.split: meta_path, rootfs_path = busybox.create_tarball( - split=True, - template=args.template.split(",")) + split=True, template=args.template.split(",") + ) with open(meta_path, "rb") as meta_fd: with open(rootfs_path, "rb") as rootfs_fd: - fingerprint = hashlib.sha256(meta_fd.read() + - rootfs_fd.read()).hexdigest() + fingerprint = hashlib.sha256( + meta_fd.read() + rootfs_fd.read() + ).hexdigest() if fingerprint in lxd.images_list(): parser.exit(1, "This image is already in the store.\n") if args.filename: - r = lxd.images_upload((meta_path, rootfs_path), args.public, - meta_path.split("/")[-1]) + r = lxd.images_upload( + (meta_path, rootfs_path), args.public, meta_path.split("/")[-1] + ) else: r = lxd.images_upload((meta_path, rootfs_path), args.public) - print("Image imported as: %s" % r['fingerprint']) + print("Image imported as: %s" % r["fingerprint"]) else: path = busybox.create_tarball(template=args.template.split(",")) @@ -361,23 +367,33 @@ if __name__ == "__main__": parser.exit(1, "This image is already in the store.\n") r = lxd.images_upload(path, args.public) - print("Image imported as: %s" % r['fingerprint']) + print("Image imported as: %s" % r["fingerprint"]) setup_alias(args.alias, fingerprint) parser = FriendlyParser(description="Import a busybox image") - parser.add_argument("--alias", action="append", - default=[], help="Aliases for the image") - parser.add_argument("--public", action="store_true", - default=False, help="Make the image public") - parser.add_argument("--split", action="store_true", - default=False, help="Whether to create a split image") - parser.add_argument("--filename", action="store_true", - default=False, help="Set the split image's filename") - parser.add_argument("--template", type=str, - default="", help="Trigger test template") - parser.add_argument("--project", type=str, - default="default", help="Project to use") + parser.add_argument( + "--alias", action="append", default=[], help="Aliases for the image" + ) + parser.add_argument( + "--public", action="store_true", default=False, help="Make the image public" + ) + parser.add_argument( + "--split", + action="store_true", + default=False, + help="Whether to create a split image", + ) + parser.add_argument( + "--filename", + action="store_true", + default=False, + help="Set the split image's filename", + ) + parser.add_argument( + "--template", type=str, default="", help="Trigger test template" + ) + parser.add_argument("--project", type=str, default="default", help="Project to use") parser.set_defaults(func=import_busybox) # Call the function From 31b050c82f60d6e4e216949eaad127bbf5656121 Mon Sep 17 00:00:00 2001 From: Simon Deziel Date: Tue, 15 Oct 2024 15:48:50 -0400 Subject: [PATCH 14/31] test/deps/import-busybox: upgrade to python 3.8+ Done with `pyupgrade --py38-plus test/deps/import-busybox` Signed-off-by: Simon Deziel (cherry picked from commit f7ec7c1dba5a649ce6eb1e31c7f8a2e6008f3afa) --- test/deps/import-busybox | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/deps/import-busybox b/test/deps/import-busybox index 1866c047e889..7dcdc7a59bbf 100755 --- a/test/deps/import-busybox +++ b/test/deps/import-busybox @@ -50,7 +50,7 @@ class UnixHTTPConnection(http.client.HTTPConnection): self.sock = sock -class LXD(object): +class LXD: workdir = None def __init__(self, path, project="default"): @@ -70,8 +70,7 @@ class LXD(object): data["project"] = self.project self.lxd.request( method, - "%s?%s" - % "&".join(["%s=%s" % (key, value) for key, value in data.items()]), + "%s?%s" % "&".join([f"{key}={value}" for key, value in data.items()]), headers, ) else: @@ -165,7 +164,7 @@ class LXD(object): return data["metadata"]["metadata"] -class BusyBox(object): +class BusyBox: workdir = None def __init__(self): From 68a0833ef14ecd49b666b267aa48fd6a323fab94 Mon Sep 17 00:00:00 2001 From: Alexander Mikhalitsyn Date: Wed, 16 Oct 2024 14:01:43 +0200 Subject: [PATCH 15/31] lxd/network/driver_ovn: allow subnets smaller than /64 We can allow IPv6 subnets smaller than /64 for the case when stateful DHCPv6 is used or DHCP is completely disabled. Suggested-by: Thomas Parrott Signed-off-by: Alexander Mikhalitsyn (cherry picked from commit 48451e838c026f6e32eeedd97277f0d12fac10f9) --- lxd/network/driver_ovn.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 5983bb215596..63795e979d2f 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -596,13 +596,16 @@ func (n *ovn) Validate(config map[string]string) error { return err } - // Check that if IPv6 enabled then the network size must be at least a /64 as both RA and DHCPv6 - // in OVN (as it generates addresses using EUI64) require at least a /64 subnet to operate. - _, ipv6Net, _ := net.ParseCIDR(config["ipv6.address"]) - if ipv6Net != nil { - ones, _ := ipv6Net.Mask.Size() - if ones > 64 { - return fmt.Errorf("IPv6 subnet must be at least a /64") + // Check that if stateless DHCPv6 is enabled and IPv6 subnet is set then the network size + // must be at least a /64 as both RA and DHCPv6 in OVN (as it generates addresses using EUI64) + // require at least a /64 subnet to operate. + if shared.IsTrueOrEmpty(config["ipv6.dhcp"]) && shared.IsFalseOrEmpty(config["ipv6.dhcp.stateful"]) { + _, ipv6Net, _ := net.ParseCIDR(config["ipv6.address"]) + if ipv6Net != nil { + ones, _ := ipv6Net.Mask.Size() + if ones > 64 { + return fmt.Errorf("IPv6 subnet must be at least a /64 when stateless DHCPv6 is enabled") + } } } From 27786408bcf2e6f9a4631c87769f8ef0acb8f7ca Mon Sep 17 00:00:00 2001 From: hamistao Date: Fri, 4 Oct 2024 12:03:41 -0300 Subject: [PATCH 16/31] api: `network_allocations_ovn_uplink` API extension Signed-off-by: hamistao (cherry picked from commit 16d32675a8498188b21a30716221e798d78a565d) --- doc/api-extensions.md | 6 ++++++ shared/version/api.go | 1 + 2 files changed, 7 insertions(+) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 4a567b11e5eb..6a4d4d4b02e5 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -2461,3 +2461,9 @@ When set to `on`, if the host has guest attachment enabled, the guest can reques This adds entity type metadata to `GET /1.0/metadata/configuration`. The entity type metadata is a JSON object under the `entities` key. + +## `network_allocations_ovn_uplink` + +Includes OVN virtual routers external IPs to `/1.0/network-allocations` responses with the type `uplink`. +Introduces the `network` field on each allocation, indicating to which network each allocated address belongs. +And lastly, adds a `project` field on leases, leases can be retrieved via `/1.0/networks//leases`. diff --git a/shared/version/api.go b/shared/version/api.go index 7da0596fb29f..67037267eb3c 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -415,6 +415,7 @@ var APIExtensions = []string{ "disk_io_bus_virtio_blk", "ubuntu_pro_guest_attach", "metadata_configuration_entity_types", + "network_allocations_ovn_uplink", } // APIExtensionsCount returns the number of available API extensions. From d7004259a8c640d7aef0eead0c240db33d9a3a08 Mon Sep 17 00:00:00 2001 From: Simon Deziel Date: Thu, 17 Oct 2024 10:07:10 -0400 Subject: [PATCH 17/31] github: use shorter job names for Trivy scanning The GitHub UI trims long names like this: > Trivy vulnerability scanner - Rep... > Trivy vulnerability scanner - Sna... > Trivy vulnerability scanner - Sna... > Trivy vulnerability scanner - Sna... > Trivy vulnerability scanner - Sna... Since the workflow title already makes it clear that Trivy is a vulnerability scanner, there is no need to repeat it. Signed-off-by: Simon Deziel (cherry picked from commit 0f31b06933d4c8942732c086d5e64ee4dd060739) --- .github/workflows/security.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index e42a7c2910dc..4df91a8583cf 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -18,7 +18,7 @@ defaults: jobs: trivy-repo: - name: Trivy vulnerability scanner - Repository + name: Trivy - Repository runs-on: ubuntu-22.04 if: ${{ ( github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' ) && github.ref_name == 'main' && github.repository == 'canonical/lxd' }} steps: @@ -49,7 +49,7 @@ jobs: ref: refs/heads/main trivy-snap: - name: Trivy vulnerability scanner - Snap + name: Trivy - Snap runs-on: ubuntu-22.04 needs: trivy-repo if: ${{ ( github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' ) && github.ref_name == 'main' && github.repository == 'canonical/lxd' }} From 858968acc5f859938a0fc47d5dd73d69ae02a8d8 Mon Sep 17 00:00:00 2001 From: Minae Lee Date: Wed, 16 Oct 2024 15:16:53 -0700 Subject: [PATCH 18/31] doc: fix minor typos Signed-off-by: Minae Lee (cherry picked from commit f83ba1c98436738fba5326bf408d75eff1ad736e) --- doc/howto/projects_create.md | 2 +- doc/howto/server_expose.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/howto/projects_create.md b/doc/howto/projects_create.md index ca744b3b0308..1ac59b05d6a1 100644 --- a/doc/howto/projects_create.md +++ b/doc/howto/projects_create.md @@ -158,7 +158,7 @@ For example: See [`PUT /1.0/projects/{name}`](swagger:/projects/project_put) for more information. ``` ```{group-tab} UI -To UI does not currently support editing the full YAML configuration for a project. +The UI does not currently support editing the full YAML configuration for a project. However, you can update several or all configuration options at the same time through the UI. ``` ```` diff --git a/doc/howto/server_expose.md b/doc/howto/server_expose.md index 5f7c3132dbb2..0000a4c0e5e5 100644 --- a/doc/howto/server_expose.md +++ b/doc/howto/server_expose.md @@ -25,7 +25,7 @@ The UI requires LXD to be exposed to the network. Therefore, you must use the CLI or API to originally expose LXD to the network. Once you have access to the UI, you can use it to update the setting. -However, be careful when changing the configured value, because using an invalid value might cause you to loose access to the UI. +However, be careful when changing the configured value, because using an invalid value might cause you to lose access to the UI. ``` Go to {guilabel}`Settings` and edit the value for `core.https_address`. From 4a273f4f1d7791cb9f6a4e9ea1b84974d3f77a2d Mon Sep 17 00:00:00 2001 From: Mark Bolton Date: Thu, 17 Oct 2024 17:38:21 -0700 Subject: [PATCH 19/31] api: Add network_ovn_uplink_vlan API extension This API extension introduces support for using bridge networks with a specified VLAN ID as the uplink for OVN networks. Signed-off-by: Mark Bolton (cherry picked from commit 8eaca0de336b8bf0f408914c5371fda543ee302c) --- 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 6a4d4d4b02e5..cfa7e3bcd889 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -2467,3 +2467,7 @@ The entity type metadata is a JSON object under the `entities` key. Includes OVN virtual routers external IPs to `/1.0/network-allocations` responses with the type `uplink`. Introduces the `network` field on each allocation, indicating to which network each allocated address belongs. And lastly, adds a `project` field on leases, leases can be retrieved via `/1.0/networks//leases`. + +## `network_ovn_uplink_vlan` + +Adds support for using a bridge network with a specified VLAN ID as an OVN uplink. diff --git a/shared/version/api.go b/shared/version/api.go index 67037267eb3c..bcabd1799b88 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -416,6 +416,7 @@ var APIExtensions = []string{ "ubuntu_pro_guest_attach", "metadata_configuration_entity_types", "network_allocations_ovn_uplink", + "network_ovn_uplink_vlan", } // APIExtensionsCount returns the number of available API extensions. From 908243836eccbe1c05f52411b36deaf129d107dc Mon Sep 17 00:00:00 2001 From: hamistao Date: Mon, 14 Oct 2024 20:04:33 -0300 Subject: [PATCH 20/31] shared/api: Add `Project` field to leases Signed-off-by: hamistao (cherry picked from commit f4ced890a3db8ddbbbbfd4f0c64448b656563935) --- shared/api/network.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shared/api/network.go b/shared/api/network.go index c4efa9375ce2..c51b6230de69 100644 --- a/shared/api/network.go +++ b/shared/api/network.go @@ -152,6 +152,12 @@ type NetworkLease struct { // // API extension: network_leases_location Location string `json:"location" yaml:"location"` + + // Name of the project of the entity related to the hostname + // Example: default + // + // API extension: network_allocations_ovn_uplink + Project string `json:"project" yaml:"project"` } // NetworkState represents the network state From 62a43a3963f5d29e0e3e1b870494687079e32ce8 Mon Sep 17 00:00:00 2001 From: hamistao Date: Tue, 15 Oct 2024 02:26:41 -0300 Subject: [PATCH 21/31] lxc/network/driver_ovn: Adapt `Leases` for ovn network This populates the new `HostProject` field accordingly and also supports calling `Leases` with an empty project name, which returns leases from all projects. Signed-off-by: hamistao (cherry picked from commit 54a0a965f5b074343efe2ffb25389ff51018aaec) --- lxd/network/driver_ovn.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 63795e979d2f..d97b338f4721 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -5437,12 +5437,13 @@ func (n *ovn) LoadBalancerDelete(listenAddress string, clientType request.Client } // Leases returns a list of leases for the OVN network. Those are directly extracted from the OVN database. +// If projectName is empty, get leases from all projects. func (n *ovn) Leases(projectName string, clientType request.ClientType) ([]api.NetworkLease, error) { var err error leases := []api.NetworkLease{} // If requested project matches network's project then include gateway IPs. - if projectName == n.project { + if projectName == n.project || projectName == "" { // Add our own gateway IPs. for _, addr := range []string{n.config["ipv4.address"], n.config["ipv6.address"]} { ip, _, _ := net.ParseCIDR(addr) @@ -5451,13 +5452,18 @@ func (n *ovn) Leases(projectName string, clientType request.ClientType) ([]api.N Hostname: fmt.Sprintf("%s.gw", n.Name()), Address: ip.String(), Type: "gateway", + Project: n.project, }) } } } // Get all the instances in the requested project that are connected to this network. - filter := dbCluster.InstanceFilter{Project: &projectName} + var filter dbCluster.InstanceFilter + if projectName != "" { + filter = dbCluster.InstanceFilter{Project: &projectName} + } + err = UsedByInstanceDevices(n.state, n.Project(), n.Name(), n.Type(), func(inst db.InstanceArgs, nicName string, nicConfig map[string]string) error { // Get the instance UUID needed for OVN port name generation. instanceUUID := inst.Config["volatile.uuid"] @@ -5491,6 +5497,7 @@ func (n *ovn) Leases(projectName string, clientType request.ClientType) ([]api.N Hwaddr: hwAddr.String(), Type: leaseType, Location: inst.Node, + Project: inst.Project, }) } From c696e146297b2bb3531c3840a069a2d5b1593229 Mon Sep 17 00:00:00 2001 From: hamistao Date: Tue, 15 Oct 2024 02:28:28 -0300 Subject: [PATCH 22/31] lxc/network/driver_bridge: Adapt `Leases` for bridge network This populates the new `HostProject` field accordingly and also supports calling `Leases` with an empty project name, which returns leases from all projects. Signed-off-by: hamistao (cherry picked from commit 5d298021bcf61db43beaaaef9e88371bb50c1252) --- lxd/network/driver_bridge.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go index 624e8a9c1353..60e7ba365fe4 100644 --- a/lxd/network/driver_bridge.go +++ b/lxd/network/driver_bridge.go @@ -3440,15 +3440,17 @@ func (n *bridge) forwardSetupFirewall() error { // Leases returns a list of leases for the bridged network. It will reach out to other cluster members as needed. // The projectName passed here refers to the initial project from the API request which may differ from the network's project. +// If projectName is empty, get leases from all projects. func (n *bridge) Leases(projectName string, clientType request.ClientType) ([]api.NetworkLease, error) { var err error var projectMacs []string + instanceProjects := make(map[string]string) leases := []api.NetworkLease{} // Get all static leases. if clientType == request.ClientTypeNormal { // If requested project matches network's project then include gateway and downstream uplink IPs. - if projectName == n.project { + if projectName == n.project || projectName == "" { // Add our own gateway IPs. for _, addr := range []string{n.config["ipv4.address"], n.config["ipv6.address"]} { ip, _, _ := net.ParseCIDR(addr) @@ -3486,6 +3488,7 @@ func (n *bridge) Leases(projectName string, clientType request.ClientType) ([]ap Hostname: fmt.Sprintf("%s-%s.uplink", projectName, network.Name), Address: v, Type: "uplink", + Project: projectName, }) } } @@ -3494,13 +3497,20 @@ func (n *bridge) Leases(projectName string, clientType request.ClientType) ([]ap } // Get all the instances in the requested project that are connected to this network. - filter := dbCluster.InstanceFilter{Project: &projectName} + var filter dbCluster.InstanceFilter + if projectName != "" { + filter = dbCluster.InstanceFilter{Project: &projectName} + } + err = UsedByInstanceDevices(n.state, n.Project(), n.Name(), n.Type(), func(inst db.InstanceArgs, nicName string, nicConfig map[string]string) error { // Fill in the hwaddr from volatile. if nicConfig["hwaddr"] == "" { nicConfig["hwaddr"] = inst.Config[fmt.Sprintf("volatile.%s.hwaddr", nicName)] } + // Keep instance project to use on dynamic leases. + instanceProjects[inst.Name] = inst.Project + // Record the MAC. hwAddr, _ := net.ParseMAC(nicConfig["hwaddr"]) if hwAddr != nil { @@ -3516,6 +3526,7 @@ func (n *bridge) Leases(projectName string, clientType request.ClientType) ([]ap Hwaddr: hwAddr.String(), Type: "static", Location: inst.Node, + Project: inst.Project, }) } @@ -3527,6 +3538,7 @@ func (n *bridge) Leases(projectName string, clientType request.ClientType) ([]ap Hwaddr: hwAddr.String(), Type: "static", Location: inst.Node, + Project: inst.Project, }) } @@ -3541,6 +3553,7 @@ func (n *bridge) Leases(projectName string, clientType request.ClientType) ([]ap Hwaddr: hwAddr.String(), Type: "dynamic", Location: inst.Node, + Project: inst.Project, }) } } @@ -3607,6 +3620,7 @@ func (n *bridge) Leases(projectName string, clientType request.ClientType) ([]ap Hwaddr: macStr, Type: "dynamic", Location: n.state.ServerName, + Project: instanceProjects[fields[3]], }) } } From 2d66eee918b0c8e45ee3512c0721bc03aa48885f Mon Sep 17 00:00:00 2001 From: hamistao Date: Tue, 15 Oct 2024 02:29:45 -0300 Subject: [PATCH 23/31] lxd/network_allocations: Get leases from all projects This is needed to also show leases for instances that are in project other than the network's project. Signed-off-by: hamistao (cherry picked from commit 29990ceabf5f6192d225acf35e5c03a15234f6f4) --- lxd/network_allocations.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/network_allocations.go b/lxd/network_allocations.go index 909e58c6c384..b9775963cffd 100644 --- a/lxd/network_allocations.go +++ b/lxd/network_allocations.go @@ -175,9 +175,9 @@ func networkAllocationsGet(d *Daemon, r *http.Request) response.Response { }) } - leases, err := n.Leases(projectName, clusterRequest.ClientTypeNormal) + leases, err := n.Leases("", clusterRequest.ClientTypeNormal) if err != nil && !errors.Is(err, network.ErrNotImplemented) { - return response.SmartError(fmt.Errorf("Failed getting leases for network %q in project %q: %w", networkName, projectName, err)) + return response.SmartError(fmt.Errorf("Failed getting leases for network %q: %w", networkName, err)) } for _, lease := range leases { @@ -189,7 +189,7 @@ func networkAllocationsGet(d *Daemon, r *http.Request) response.Response { result = append(result, api.NetworkAllocations{ Address: cidrAddr, - UsedBy: api.NewURL().Path(version.APIVersion, "instances", lease.Hostname).Project(projectName).String(), + UsedBy: api.NewURL().Path(version.APIVersion, "instances", lease.Hostname).Project(lease.Project).String(), Type: "instance", Hwaddr: lease.Hwaddr, NAT: nat, From da8b2e0bca549490760975dab4ee7c12d54761cd Mon Sep 17 00:00:00 2001 From: hamistao Date: Fri, 4 Oct 2024 09:52:22 -0300 Subject: [PATCH 24/31] lxd/network_allocations: Include OVN uplink allocations This also uses the correct project for instance leases, instead of assuming the instance is always on the same project as the network. Signed-off-by: hamistao (cherry picked from commit 3fcdc762e8b78ca3ef7b1f89a9084be453f73d4b) --- lxd/network_allocations.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lxd/network_allocations.go b/lxd/network_allocations.go index b9775963cffd..c66435240820 100644 --- a/lxd/network_allocations.go +++ b/lxd/network_allocations.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "net/http" + "strings" "github.com/canonical/lxd/lxd/auth" clusterRequest "github.com/canonical/lxd/lxd/cluster/request" @@ -29,7 +30,7 @@ var networkAllocationsCmd = APIEndpoint{ // swagger:operation GET /1.0/network-allocations network-allocations network_allocations_get // -// Get the network allocations in use (`network`, `network-forward` and `load-balancer` and `instance`) +// Get the network allocations in use (`network`, `network-forward`, `load-balancer`, `uplink` and `instance`) // // Returns a list of network allocations in use by a LXD deployment. // @@ -181,16 +182,26 @@ func networkAllocationsGet(d *Daemon, r *http.Request) response.Response { } for _, lease := range leases { - if shared.ValueInSlice(lease.Type, []string{"static", "dynamic"}) { + if shared.ValueInSlice(lease.Type, []string{"static", "dynamic", "uplink"}) { cidrAddr, nat, err := ipToCIDR(lease.Address, netConf) if err != nil { return response.SmartError(err) } + var allocationType, usedBy string + if lease.Type == "uplink" { + allocationType = "uplink" + networkName := strings.TrimSuffix(strings.TrimPrefix(lease.Hostname, lease.Project+"-"), ".uplink") + usedBy = api.NewURL().Path(version.APIVersion, "networks", networkName).Project(lease.Project).String() + } else { + allocationType = "instance" + usedBy = api.NewURL().Path(version.APIVersion, "instances", lease.Hostname).Project(lease.Project).String() + } + result = append(result, api.NetworkAllocations{ Address: cidrAddr, - UsedBy: api.NewURL().Path(version.APIVersion, "instances", lease.Hostname).Project(lease.Project).String(), - Type: "instance", + UsedBy: usedBy, + Type: allocationType, Hwaddr: lease.Hwaddr, NAT: nat, }) From 5552c05a77af721d85ddca66432fa5b3fdff4f0c Mon Sep 17 00:00:00 2001 From: hamistao Date: Fri, 4 Oct 2024 12:15:42 -0300 Subject: [PATCH 25/31] shared/api: Add `Network` to `NetworkAllocations` Also puts a full stop in comments Signed-off-by: hamistao (cherry picked from commit c2080bd5b38a2933d844571c21bb70da76106b82) --- shared/api/network_addresses.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/api/network_addresses.go b/shared/api/network_addresses.go index bfecfdf342e3..e3a3978b0d78 100644 --- a/shared/api/network_addresses.go +++ b/shared/api/network_addresses.go @@ -18,4 +18,6 @@ type NetworkAllocations struct { NAT bool `json:"nat" yaml:"nat"` // Hwaddr is the MAC address of the entity consuming the network address Hwaddr string `json:"hwaddr" yaml:"hwaddr"` + // Network is the name of the network the allocated address belongs to + Network string `json:"network" yaml:"network"` } From ebfc9c46dbfcc24b35aa49de2318cccd092c5944 Mon Sep 17 00:00:00 2001 From: hamistao Date: Fri, 4 Oct 2024 12:16:03 -0300 Subject: [PATCH 26/31] lxd/network_allocations: Populate `Network` field Signed-off-by: hamistao (cherry picked from commit cb2b230faa2016629fc129d19393a1fd6b3cbc9f) --- lxd/network_allocations.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lxd/network_allocations.go b/lxd/network_allocations.go index c66435240820..01f830226885 100644 --- a/lxd/network_allocations.go +++ b/lxd/network_allocations.go @@ -173,6 +173,7 @@ func networkAllocationsGet(d *Daemon, r *http.Request) response.Response { UsedBy: api.NewURL().Path(version.APIVersion, "networks", networkName).Project(projectName).String(), Type: "network", NAT: shared.IsTrue(netConf[fmt.Sprintf("%s.nat", keyPrefix)]), + Network: networkName, }) } @@ -204,6 +205,7 @@ func networkAllocationsGet(d *Daemon, r *http.Request) response.Response { Type: allocationType, Hwaddr: lease.Hwaddr, NAT: nat, + Network: networkName, }) } } @@ -232,6 +234,7 @@ func networkAllocationsGet(d *Daemon, r *http.Request) response.Response { UsedBy: api.NewURL().Path(version.APIVersion, "networks", networkName, "forwards", forward.ListenAddress).Project(projectName).String(), Type: "network-forward", NAT: false, // Network forwards are ingress and so aren't affected by SNAT. + Network: networkName, }, ) } @@ -260,6 +263,7 @@ func networkAllocationsGet(d *Daemon, r *http.Request) response.Response { UsedBy: api.NewURL().Path(version.APIVersion, "networks", networkName, "load-balancers", loadBalancer.ListenAddress).Project(projectName).String(), Type: "network-load-balancer", NAT: false, // Network load-balancers are ingress and so aren't affected by SNAT. + Network: networkName, }, ) } From ebedd188938e6902d605a12dbc0812f63fa1264d Mon Sep 17 00:00:00 2001 From: hamistao Date: Fri, 4 Oct 2024 12:25:23 -0300 Subject: [PATCH 27/31] lxc: Show allocation network Signed-off-by: hamistao (cherry picked from commit 18a8a330c92e77c1605c631643094008a293e731) --- lxc/network_allocations.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lxc/network_allocations.go b/lxc/network_allocations.go index d790443164cb..9c0e98e3e09c 100644 --- a/lxc/network_allocations.go +++ b/lxc/network_allocations.go @@ -23,6 +23,7 @@ func (c *cmdNetworkListAllocations) pretty(allocs []api.NetworkAllocations) erro header := []string{ i18n.G("USED BY"), i18n.G("ADDRESS"), + i18n.G("NETWORK"), i18n.G("TYPE"), i18n.G("NAT"), i18n.G("HARDWARE ADDRESS"), @@ -33,6 +34,7 @@ func (c *cmdNetworkListAllocations) pretty(allocs []api.NetworkAllocations) erro row := []string{ alloc.UsedBy, alloc.Address, + alloc.Network, alloc.Type, fmt.Sprint(alloc.NAT), alloc.Hwaddr, From 6898ca6bd72d4de6adcd5fd95422b93cd3d951e4 Mon Sep 17 00:00:00 2001 From: hamistao Date: Tue, 15 Oct 2024 02:34:29 -0300 Subject: [PATCH 28/31] doc: Run `make update-api` Signed-off-by: hamistao (cherry picked from commit 650098a2d9c74c7862922c126ad5e61de3008c76) --- doc/rest-api.yaml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/rest-api.yaml b/doc/rest-api.yaml index a25763134b2e..4b9c42cb2655 100644 --- a/doc/rest-api.yaml +++ b/doc/rest-api.yaml @@ -3139,6 +3139,10 @@ definitions: description: Whether the entity comes from a network that LXD performs egress source NAT on type: boolean x-go-name: NAT + network: + description: Network is the name of the network the allocated address belongs to + type: string + x-go-name: Network type: description: Type of the entity consuming the network address type: string @@ -3289,6 +3293,11 @@ definitions: example: lxd01 type: string x-go-name: Location + project: + description: Name of the project of the entity related to the hostname + example: default + type: string + x-go-name: Project type: description: The type of record (static or dynamic) example: dynamic @@ -11882,7 +11891,7 @@ paths: $ref: '#/responses/Forbidden' "500": $ref: '#/responses/InternalServerError' - summary: Get the network allocations in use (`network`, `network-forward` and `load-balancer` and `instance`) + summary: Get the network allocations in use (`network`, `network-forward`, `load-balancer`, `uplink` and `instance`) tags: - network-allocations /1.0/network-zones: From 7fdf043e5d1fcc4d746882ff0bf1ec3a95252d05 Mon Sep 17 00:00:00 2001 From: hamistao Date: Tue, 15 Oct 2024 02:24:40 -0300 Subject: [PATCH 29/31] test: Test showing instances from different projects Signed-off-by: hamistao (cherry picked from commit 7dd9908ebe15022ab1c8d825ef9808283e00a0b9) --- test/suites/network.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/suites/network.sh b/test/suites/network.sh index c99367dafff5..295556f8b2fb 100644 --- a/test/suites/network.sh +++ b/test/suites/network.sh @@ -91,8 +91,18 @@ test_network() { grep -q "${v6_addr}.*nettest" "${LXD_DIR}/networks/lxdt$$/dnsmasq.hosts/nettest.eth0" lxc start nettest + # Create new project with an instance with ipv[46] for the next tests. + lxc project create foo -c features.networks=false -c features.images=false -c features.profiles=false + lxc launch testimage outsider -n lxdt$$ --project foo + v4_addr_foo="$(lxc network get lxdt$$ ipv4.address | cut -d/ -f1)1" + v6_addr_foo="$(lxc network get lxdt$$ ipv6.address | cut -d/ -f1)01" + lxc config device set outsider eth0 ipv4.address "${v4_addr_foo}" --project foo + lxc config device set outsider eth0 ipv6.address "${v6_addr_foo}" --project foo + lxc network list-leases lxdt$$ | grep STATIC | grep -q "${v4_addr}" lxc network list-leases lxdt$$ | grep STATIC | grep -q "${v6_addr}" + lxc network list-leases lxdt$$ --project foo | grep STATIC | grep -q "${v4_addr_foo}" + lxc network list-leases lxdt$$ --project foo | grep STATIC | grep -q "${v6_addr_foo}" # Request DHCPv6 lease (if udhcpc6 is in busybox image). busyboxUdhcpc6=1 @@ -114,7 +124,11 @@ test_network() { lxc network list-allocations localhost: | grep -e "${net_ipv4}" -e "${net_ipv6}" lxc network list-allocations localhost: | grep -e "/1.0/networks/lxdt$$" -e "/1.0/instances/nettest" lxc network list-allocations localhost: | grep -e "${v4_addr}" -e "${v6_addr}" + lxc network list-allocations --format csv | grep -q "/1.0/instances/outsider?project=foo,${v4_addr_foo}" + lxc network list-allocations --format csv | grep -q "/1.0/instances/outsider?project=foo,${v6_addr_foo}" + lxc delete -f outsider --project foo + lxc project delete foo lxc delete nettest -f lxc network delete lxdt$$ } From d927d468464bdc8e252b4fd210c3cda763d0d3e8 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 18 Oct 2024 09:58:56 +0100 Subject: [PATCH 30/31] i18n: Update translation templates. Signed-off-by: Thomas Parrott --- po/lxd.pot | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/po/lxd.pot b/po/lxd.pot index 56dec9d197aa..54c33e8d494e 100644 --- a/po/lxd.pot +++ b/po/lxd.pot @@ -7,7 +7,7 @@ msgid "" msgstr "Project-Id-Version: lxd\n" "Report-Msgid-Bugs-To: lxd@lists.canonical.com\n" - "POT-Creation-Date: 2024-10-16 10:01+0000\n" + "POT-Creation-Date: 2024-10-18 09:58+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1547,7 +1547,7 @@ msgstr "" msgid "Delete warning" msgstr "" -#: lxc/action.go:33 lxc/action.go:54 lxc/action.go:76 lxc/action.go:99 lxc/alias.go:23 lxc/alias.go:60 lxc/alias.go:110 lxc/alias.go:159 lxc/alias.go:214 lxc/auth.go:31 lxc/auth.go:60 lxc/auth.go:99 lxc/auth.go:153 lxc/auth.go:202 lxc/auth.go:333 lxc/auth.go:393 lxc/auth.go:442 lxc/auth.go:494 lxc/auth.go:517 lxc/auth.go:576 lxc/auth.go:732 lxc/auth.go:766 lxc/auth.go:833 lxc/auth.go:896 lxc/auth.go:957 lxc/auth.go:1085 lxc/auth.go:1108 lxc/auth.go:1166 lxc/auth.go:1235 lxc/auth.go:1257 lxc/auth.go:1435 lxc/auth.go:1473 lxc/auth.go:1525 lxc/auth.go:1574 lxc/auth.go:1693 lxc/auth.go:1753 lxc/auth.go:1802 lxc/auth.go:1853 lxc/auth.go:1876 lxc/auth.go:1929 lxc/cluster.go:30 lxc/cluster.go:123 lxc/cluster.go:207 lxc/cluster.go:256 lxc/cluster.go:307 lxc/cluster.go:368 lxc/cluster.go:440 lxc/cluster.go:472 lxc/cluster.go:522 lxc/cluster.go:605 lxc/cluster.go:690 lxc/cluster.go:805 lxc/cluster.go:881 lxc/cluster.go:983 lxc/cluster.go:1062 lxc/cluster.go:1169 lxc/cluster.go:1191 lxc/cluster_group.go:31 lxc/cluster_group.go:85 lxc/cluster_group.go:158 lxc/cluster_group.go:236 lxc/cluster_group.go:288 lxc/cluster_group.go:404 lxc/cluster_group.go:478 lxc/cluster_group.go:551 lxc/cluster_group.go:599 lxc/cluster_group.go:653 lxc/cluster_role.go:24 lxc/cluster_role.go:51 lxc/cluster_role.go:107 lxc/config.go:33 lxc/config.go:100 lxc/config.go:385 lxc/config.go:518 lxc/config.go:735 lxc/config.go:859 lxc/config.go:894 lxc/config.go:934 lxc/config.go:989 lxc/config.go:1080 lxc/config.go:1111 lxc/config.go:1165 lxc/config_device.go:25 lxc/config_device.go:79 lxc/config_device.go:209 lxc/config_device.go:286 lxc/config_device.go:357 lxc/config_device.go:451 lxc/config_device.go:549 lxc/config_device.go:556 lxc/config_device.go:669 lxc/config_device.go:742 lxc/config_metadata.go:28 lxc/config_metadata.go:56 lxc/config_metadata.go:181 lxc/config_template.go:28 lxc/config_template.go:68 lxc/config_template.go:111 lxc/config_template.go:153 lxc/config_template.go:241 lxc/config_template.go:301 lxc/config_trust.go:34 lxc/config_trust.go:87 lxc/config_trust.go:236 lxc/config_trust.go:350 lxc/config_trust.go:432 lxc/config_trust.go:534 lxc/config_trust.go:580 lxc/config_trust.go:651 lxc/console.go:37 lxc/copy.go:42 lxc/delete.go:32 lxc/exec.go:41 lxc/export.go:32 lxc/file.go:88 lxc/file.go:135 lxc/file.go:313 lxc/file.go:362 lxc/file.go:432 lxc/file.go:657 lxc/file.go:1176 lxc/image.go:38 lxc/image.go:159 lxc/image.go:325 lxc/image.go:380 lxc/image.go:501 lxc/image.go:665 lxc/image.go:904 lxc/image.go:1038 lxc/image.go:1357 lxc/image.go:1444 lxc/image.go:1502 lxc/image.go:1553 lxc/image.go:1608 lxc/image_alias.go:24 lxc/image_alias.go:60 lxc/image_alias.go:107 lxc/image_alias.go:152 lxc/image_alias.go:255 lxc/import.go:29 lxc/info.go:33 lxc/init.go:44 lxc/launch.go:24 lxc/list.go:49 lxc/main.go:83 lxc/manpage.go:22 lxc/monitor.go:34 lxc/move.go:38 lxc/network.go:33 lxc/network.go:136 lxc/network.go:221 lxc/network.go:294 lxc/network.go:373 lxc/network.go:423 lxc/network.go:508 lxc/network.go:593 lxc/network.go:721 lxc/network.go:790 lxc/network.go:913 lxc/network.go:1006 lxc/network.go:1077 lxc/network.go:1129 lxc/network.go:1217 lxc/network.go:1281 lxc/network_acl.go:30 lxc/network_acl.go:95 lxc/network_acl.go:166 lxc/network_acl.go:219 lxc/network_acl.go:267 lxc/network_acl.go:328 lxc/network_acl.go:417 lxc/network_acl.go:497 lxc/network_acl.go:527 lxc/network_acl.go:658 lxc/network_acl.go:707 lxc/network_acl.go:756 lxc/network_acl.go:771 lxc/network_acl.go:892 lxc/network_allocations.go:51 lxc/network_forward.go:33 lxc/network_forward.go:90 lxc/network_forward.go:171 lxc/network_forward.go:236 lxc/network_forward.go:384 lxc/network_forward.go:453 lxc/network_forward.go:551 lxc/network_forward.go:581 lxc/network_forward.go:723 lxc/network_forward.go:785 lxc/network_forward.go:800 lxc/network_forward.go:865 lxc/network_load_balancer.go:33 lxc/network_load_balancer.go:94 lxc/network_load_balancer.go:173 lxc/network_load_balancer.go:238 lxc/network_load_balancer.go:388 lxc/network_load_balancer.go:456 lxc/network_load_balancer.go:554 lxc/network_load_balancer.go:584 lxc/network_load_balancer.go:727 lxc/network_load_balancer.go:788 lxc/network_load_balancer.go:803 lxc/network_load_balancer.go:867 lxc/network_load_balancer.go:953 lxc/network_load_balancer.go:968 lxc/network_load_balancer.go:1029 lxc/network_peer.go:29 lxc/network_peer.go:82 lxc/network_peer.go:159 lxc/network_peer.go:216 lxc/network_peer.go:332 lxc/network_peer.go:400 lxc/network_peer.go:489 lxc/network_peer.go:519 lxc/network_peer.go:644 lxc/network_zone.go:29 lxc/network_zone.go:86 lxc/network_zone.go:157 lxc/network_zone.go:212 lxc/network_zone.go:272 lxc/network_zone.go:359 lxc/network_zone.go:439 lxc/network_zone.go:470 lxc/network_zone.go:589 lxc/network_zone.go:637 lxc/network_zone.go:694 lxc/network_zone.go:764 lxc/network_zone.go:816 lxc/network_zone.go:875 lxc/network_zone.go:961 lxc/network_zone.go:1037 lxc/network_zone.go:1067 lxc/network_zone.go:1185 lxc/network_zone.go:1234 lxc/network_zone.go:1249 lxc/network_zone.go:1295 lxc/operation.go:25 lxc/operation.go:57 lxc/operation.go:107 lxc/operation.go:194 lxc/profile.go:30 lxc/profile.go:105 lxc/profile.go:168 lxc/profile.go:251 lxc/profile.go:321 lxc/profile.go:395 lxc/profile.go:445 lxc/profile.go:573 lxc/profile.go:634 lxc/profile.go:695 lxc/profile.go:771 lxc/profile.go:823 lxc/profile.go:899 lxc/profile.go:955 lxc/project.go:30 lxc/project.go:94 lxc/project.go:182 lxc/project.go:245 lxc/project.go:373 lxc/project.go:434 lxc/project.go:547 lxc/project.go:604 lxc/project.go:683 lxc/project.go:714 lxc/project.go:767 lxc/project.go:826 lxc/publish.go:34 lxc/query.go:34 lxc/rebuild.go:28 lxc/remote.go:35 lxc/remote.go:91 lxc/remote.go:701 lxc/remote.go:739 lxc/remote.go:825 lxc/remote.go:898 lxc/remote.go:954 lxc/remote.go:994 lxc/rename.go:22 lxc/restore.go:24 lxc/snapshot.go:32 lxc/storage.go:34 lxc/storage.go:97 lxc/storage.go:195 lxc/storage.go:245 lxc/storage.go:369 lxc/storage.go:439 lxc/storage.go:611 lxc/storage.go:690 lxc/storage.go:786 lxc/storage.go:872 lxc/storage_bucket.go:30 lxc/storage_bucket.go:84 lxc/storage_bucket.go:189 lxc/storage_bucket.go:250 lxc/storage_bucket.go:383 lxc/storage_bucket.go:459 lxc/storage_bucket.go:536 lxc/storage_bucket.go:630 lxc/storage_bucket.go:699 lxc/storage_bucket.go:733 lxc/storage_bucket.go:774 lxc/storage_bucket.go:853 lxc/storage_bucket.go:959 lxc/storage_bucket.go:1023 lxc/storage_bucket.go:1158 lxc/storage_volume.go:44 lxc/storage_volume.go:166 lxc/storage_volume.go:264 lxc/storage_volume.go:355 lxc/storage_volume.go:558 lxc/storage_volume.go:659 lxc/storage_volume.go:734 lxc/storage_volume.go:816 lxc/storage_volume.go:897 lxc/storage_volume.go:1106 lxc/storage_volume.go:1221 lxc/storage_volume.go:1368 lxc/storage_volume.go:1452 lxc/storage_volume.go:1697 lxc/storage_volume.go:1778 lxc/storage_volume.go:1893 lxc/storage_volume.go:2037 lxc/storage_volume.go:2146 lxc/storage_volume.go:2192 lxc/storage_volume.go:2315 lxc/storage_volume.go:2382 lxc/storage_volume.go:2536 lxc/version.go:22 lxc/warning.go:30 lxc/warning.go:72 lxc/warning.go:263 lxc/warning.go:304 lxc/warning.go:358 +#: lxc/action.go:33 lxc/action.go:54 lxc/action.go:76 lxc/action.go:99 lxc/alias.go:23 lxc/alias.go:60 lxc/alias.go:110 lxc/alias.go:159 lxc/alias.go:214 lxc/auth.go:31 lxc/auth.go:60 lxc/auth.go:99 lxc/auth.go:153 lxc/auth.go:202 lxc/auth.go:333 lxc/auth.go:393 lxc/auth.go:442 lxc/auth.go:494 lxc/auth.go:517 lxc/auth.go:576 lxc/auth.go:732 lxc/auth.go:766 lxc/auth.go:833 lxc/auth.go:896 lxc/auth.go:957 lxc/auth.go:1085 lxc/auth.go:1108 lxc/auth.go:1166 lxc/auth.go:1235 lxc/auth.go:1257 lxc/auth.go:1435 lxc/auth.go:1473 lxc/auth.go:1525 lxc/auth.go:1574 lxc/auth.go:1693 lxc/auth.go:1753 lxc/auth.go:1802 lxc/auth.go:1853 lxc/auth.go:1876 lxc/auth.go:1929 lxc/cluster.go:30 lxc/cluster.go:123 lxc/cluster.go:207 lxc/cluster.go:256 lxc/cluster.go:307 lxc/cluster.go:368 lxc/cluster.go:440 lxc/cluster.go:472 lxc/cluster.go:522 lxc/cluster.go:605 lxc/cluster.go:690 lxc/cluster.go:805 lxc/cluster.go:881 lxc/cluster.go:983 lxc/cluster.go:1062 lxc/cluster.go:1169 lxc/cluster.go:1191 lxc/cluster_group.go:31 lxc/cluster_group.go:85 lxc/cluster_group.go:158 lxc/cluster_group.go:236 lxc/cluster_group.go:288 lxc/cluster_group.go:404 lxc/cluster_group.go:478 lxc/cluster_group.go:551 lxc/cluster_group.go:599 lxc/cluster_group.go:653 lxc/cluster_role.go:24 lxc/cluster_role.go:51 lxc/cluster_role.go:107 lxc/config.go:33 lxc/config.go:100 lxc/config.go:385 lxc/config.go:518 lxc/config.go:735 lxc/config.go:859 lxc/config.go:894 lxc/config.go:934 lxc/config.go:989 lxc/config.go:1080 lxc/config.go:1111 lxc/config.go:1165 lxc/config_device.go:25 lxc/config_device.go:79 lxc/config_device.go:209 lxc/config_device.go:286 lxc/config_device.go:357 lxc/config_device.go:451 lxc/config_device.go:549 lxc/config_device.go:556 lxc/config_device.go:669 lxc/config_device.go:742 lxc/config_metadata.go:28 lxc/config_metadata.go:56 lxc/config_metadata.go:181 lxc/config_template.go:28 lxc/config_template.go:68 lxc/config_template.go:111 lxc/config_template.go:153 lxc/config_template.go:241 lxc/config_template.go:301 lxc/config_trust.go:34 lxc/config_trust.go:87 lxc/config_trust.go:236 lxc/config_trust.go:350 lxc/config_trust.go:432 lxc/config_trust.go:534 lxc/config_trust.go:580 lxc/config_trust.go:651 lxc/console.go:37 lxc/copy.go:42 lxc/delete.go:32 lxc/exec.go:41 lxc/export.go:32 lxc/file.go:88 lxc/file.go:135 lxc/file.go:313 lxc/file.go:362 lxc/file.go:432 lxc/file.go:657 lxc/file.go:1176 lxc/image.go:38 lxc/image.go:159 lxc/image.go:325 lxc/image.go:380 lxc/image.go:501 lxc/image.go:665 lxc/image.go:904 lxc/image.go:1038 lxc/image.go:1357 lxc/image.go:1444 lxc/image.go:1502 lxc/image.go:1553 lxc/image.go:1608 lxc/image_alias.go:24 lxc/image_alias.go:60 lxc/image_alias.go:107 lxc/image_alias.go:152 lxc/image_alias.go:255 lxc/import.go:29 lxc/info.go:33 lxc/init.go:44 lxc/launch.go:24 lxc/list.go:49 lxc/main.go:83 lxc/manpage.go:22 lxc/monitor.go:34 lxc/move.go:38 lxc/network.go:33 lxc/network.go:136 lxc/network.go:221 lxc/network.go:294 lxc/network.go:373 lxc/network.go:423 lxc/network.go:508 lxc/network.go:593 lxc/network.go:721 lxc/network.go:790 lxc/network.go:913 lxc/network.go:1006 lxc/network.go:1077 lxc/network.go:1129 lxc/network.go:1217 lxc/network.go:1281 lxc/network_acl.go:30 lxc/network_acl.go:95 lxc/network_acl.go:166 lxc/network_acl.go:219 lxc/network_acl.go:267 lxc/network_acl.go:328 lxc/network_acl.go:417 lxc/network_acl.go:497 lxc/network_acl.go:527 lxc/network_acl.go:658 lxc/network_acl.go:707 lxc/network_acl.go:756 lxc/network_acl.go:771 lxc/network_acl.go:892 lxc/network_allocations.go:53 lxc/network_forward.go:33 lxc/network_forward.go:90 lxc/network_forward.go:171 lxc/network_forward.go:236 lxc/network_forward.go:384 lxc/network_forward.go:453 lxc/network_forward.go:551 lxc/network_forward.go:581 lxc/network_forward.go:723 lxc/network_forward.go:785 lxc/network_forward.go:800 lxc/network_forward.go:865 lxc/network_load_balancer.go:33 lxc/network_load_balancer.go:94 lxc/network_load_balancer.go:173 lxc/network_load_balancer.go:238 lxc/network_load_balancer.go:388 lxc/network_load_balancer.go:456 lxc/network_load_balancer.go:554 lxc/network_load_balancer.go:584 lxc/network_load_balancer.go:727 lxc/network_load_balancer.go:788 lxc/network_load_balancer.go:803 lxc/network_load_balancer.go:867 lxc/network_load_balancer.go:953 lxc/network_load_balancer.go:968 lxc/network_load_balancer.go:1029 lxc/network_peer.go:29 lxc/network_peer.go:82 lxc/network_peer.go:159 lxc/network_peer.go:216 lxc/network_peer.go:332 lxc/network_peer.go:400 lxc/network_peer.go:489 lxc/network_peer.go:519 lxc/network_peer.go:644 lxc/network_zone.go:29 lxc/network_zone.go:86 lxc/network_zone.go:157 lxc/network_zone.go:212 lxc/network_zone.go:272 lxc/network_zone.go:359 lxc/network_zone.go:439 lxc/network_zone.go:470 lxc/network_zone.go:589 lxc/network_zone.go:637 lxc/network_zone.go:694 lxc/network_zone.go:764 lxc/network_zone.go:816 lxc/network_zone.go:875 lxc/network_zone.go:961 lxc/network_zone.go:1037 lxc/network_zone.go:1067 lxc/network_zone.go:1185 lxc/network_zone.go:1234 lxc/network_zone.go:1249 lxc/network_zone.go:1295 lxc/operation.go:25 lxc/operation.go:57 lxc/operation.go:107 lxc/operation.go:194 lxc/profile.go:30 lxc/profile.go:105 lxc/profile.go:168 lxc/profile.go:251 lxc/profile.go:321 lxc/profile.go:395 lxc/profile.go:445 lxc/profile.go:573 lxc/profile.go:634 lxc/profile.go:695 lxc/profile.go:771 lxc/profile.go:823 lxc/profile.go:899 lxc/profile.go:955 lxc/project.go:30 lxc/project.go:94 lxc/project.go:182 lxc/project.go:245 lxc/project.go:373 lxc/project.go:434 lxc/project.go:547 lxc/project.go:604 lxc/project.go:683 lxc/project.go:714 lxc/project.go:767 lxc/project.go:826 lxc/publish.go:34 lxc/query.go:34 lxc/rebuild.go:28 lxc/remote.go:35 lxc/remote.go:91 lxc/remote.go:701 lxc/remote.go:739 lxc/remote.go:825 lxc/remote.go:898 lxc/remote.go:954 lxc/remote.go:994 lxc/rename.go:22 lxc/restore.go:24 lxc/snapshot.go:32 lxc/storage.go:34 lxc/storage.go:97 lxc/storage.go:195 lxc/storage.go:245 lxc/storage.go:369 lxc/storage.go:439 lxc/storage.go:611 lxc/storage.go:690 lxc/storage.go:786 lxc/storage.go:872 lxc/storage_bucket.go:30 lxc/storage_bucket.go:84 lxc/storage_bucket.go:189 lxc/storage_bucket.go:250 lxc/storage_bucket.go:383 lxc/storage_bucket.go:459 lxc/storage_bucket.go:536 lxc/storage_bucket.go:630 lxc/storage_bucket.go:699 lxc/storage_bucket.go:733 lxc/storage_bucket.go:774 lxc/storage_bucket.go:853 lxc/storage_bucket.go:959 lxc/storage_bucket.go:1023 lxc/storage_bucket.go:1158 lxc/storage_volume.go:44 lxc/storage_volume.go:166 lxc/storage_volume.go:264 lxc/storage_volume.go:355 lxc/storage_volume.go:558 lxc/storage_volume.go:659 lxc/storage_volume.go:734 lxc/storage_volume.go:816 lxc/storage_volume.go:897 lxc/storage_volume.go:1106 lxc/storage_volume.go:1221 lxc/storage_volume.go:1368 lxc/storage_volume.go:1452 lxc/storage_volume.go:1697 lxc/storage_volume.go:1778 lxc/storage_volume.go:1893 lxc/storage_volume.go:2037 lxc/storage_volume.go:2146 lxc/storage_volume.go:2192 lxc/storage_volume.go:2315 lxc/storage_volume.go:2382 lxc/storage_volume.go:2536 lxc/version.go:22 lxc/warning.go:30 lxc/warning.go:72 lxc/warning.go:263 lxc/warning.go:304 lxc/warning.go:358 msgid "Description" msgstr "" @@ -2178,7 +2178,7 @@ msgid "Forcefully removing a server from the cluster should only be done as a "Are you really sure you want to force removing %s? (yes/no): " msgstr "" -#: lxc/alias.go:112 lxc/auth.go:337 lxc/auth.go:770 lxc/auth.go:1697 lxc/cluster.go:125 lxc/cluster.go:882 lxc/cluster_group.go:406 lxc/config_template.go:243 lxc/config_trust.go:352 lxc/config_trust.go:434 lxc/image.go:1064 lxc/image_alias.go:157 lxc/list.go:133 lxc/network.go:917 lxc/network.go:1008 lxc/network_acl.go:98 lxc/network_allocations.go:57 lxc/network_forward.go:93 lxc/network_load_balancer.go:97 lxc/network_peer.go:85 lxc/network_zone.go:89 lxc/network_zone.go:697 lxc/operation.go:109 lxc/profile.go:638 lxc/project.go:436 lxc/project.go:828 lxc/remote.go:743 lxc/storage.go:613 lxc/storage_bucket.go:460 lxc/storage_bucket.go:775 lxc/storage_volume.go:1469 lxc/warning.go:94 +#: lxc/alias.go:112 lxc/auth.go:337 lxc/auth.go:770 lxc/auth.go:1697 lxc/cluster.go:125 lxc/cluster.go:882 lxc/cluster_group.go:406 lxc/config_template.go:243 lxc/config_trust.go:352 lxc/config_trust.go:434 lxc/image.go:1064 lxc/image_alias.go:157 lxc/list.go:133 lxc/network.go:917 lxc/network.go:1008 lxc/network_acl.go:98 lxc/network_allocations.go:59 lxc/network_forward.go:93 lxc/network_load_balancer.go:97 lxc/network_peer.go:85 lxc/network_zone.go:89 lxc/network_zone.go:697 lxc/operation.go:109 lxc/profile.go:638 lxc/project.go:436 lxc/project.go:828 lxc/remote.go:743 lxc/storage.go:613 lxc/storage_bucket.go:460 lxc/storage_bucket.go:775 lxc/storage_volume.go:1469 lxc/warning.go:94 msgid "Format (csv|json|table|yaml|compact)" msgstr "" @@ -2394,7 +2394,7 @@ msgstr "" msgid "Group ID to run the command as (default 0)" msgstr "" -#: lxc/network_allocations.go:28 +#: lxc/network_allocations.go:29 msgid "HARDWARE ADDRESS" msgstr "" @@ -3043,7 +3043,7 @@ msgid "List instances\n" " Defaults to -1 (unlimited). Use 0 to limit to the column header size." msgstr "" -#: lxc/network_allocations.go:50 lxc/network_allocations.go:51 +#: lxc/network_allocations.go:52 lxc/network_allocations.go:53 msgid "List network allocations in use" msgstr "" @@ -3666,10 +3666,14 @@ msgstr "" msgid "NAME" msgstr "" -#: lxc/network_allocations.go:27 +#: lxc/network_allocations.go:28 msgid "NAT" msgstr "" +#: lxc/network_allocations.go:26 +msgid "NETWORK" +msgstr "" + #: lxc/project.go:528 msgid "NETWORK ZONES" msgstr "" @@ -4595,7 +4599,7 @@ msgstr "" msgid "Role (admin or read-only)" msgstr "" -#: lxc/network_allocations.go:58 +#: lxc/network_allocations.go:60 msgid "Run again a specific project" msgstr "" @@ -4603,7 +4607,7 @@ msgstr "" msgid "Run against all instances" msgstr "" -#: lxc/network_allocations.go:59 +#: lxc/network_allocations.go:61 msgid "Run against all projects" msgstr "" @@ -5334,7 +5338,7 @@ msgstr "" msgid "TOKEN" msgstr "" -#: lxc/auth.go:815 lxc/config_trust.go:408 lxc/image.go:1081 lxc/image_alias.go:236 lxc/list.go:571 lxc/network.go:982 lxc/network.go:1056 lxc/network_allocations.go:26 lxc/operation.go:172 lxc/storage_volume.go:1583 lxc/warning.go:216 +#: lxc/auth.go:815 lxc/config_trust.go:408 lxc/image.go:1081 lxc/image_alias.go:236 lxc/list.go:571 lxc/network.go:982 lxc/network.go:1056 lxc/network_allocations.go:27 lxc/operation.go:172 lxc/storage_volume.go:1583 lxc/warning.go:216 msgid "TYPE" msgstr "" From 6a1ff7f4b7a4093dffcaff486cbf3c583a7904e8 Mon Sep 17 00:00:00 2001 From: Thomas Parrott Date: Fri, 18 Oct 2024 09:58:58 +0100 Subject: [PATCH 31/31] i18n: Update translations. Signed-off-by: Thomas Parrott --- po/ar.po | 22 +++++++++++++--------- po/ber.po | 22 +++++++++++++--------- po/bg.po | 22 +++++++++++++--------- po/ca.po | 22 +++++++++++++--------- po/cs.po | 22 +++++++++++++--------- po/de.po | 22 +++++++++++++--------- po/el.po | 22 +++++++++++++--------- po/eo.po | 22 +++++++++++++--------- po/es.po | 22 +++++++++++++--------- po/fa.po | 22 +++++++++++++--------- po/fi.po | 22 +++++++++++++--------- po/fr.po | 22 +++++++++++++--------- po/he.po | 22 +++++++++++++--------- po/hi.po | 22 +++++++++++++--------- po/id.po | 22 +++++++++++++--------- po/it.po | 22 +++++++++++++--------- po/ja.po | 23 ++++++++++++++--------- po/ka.po | 22 +++++++++++++--------- po/ko.po | 22 +++++++++++++--------- po/mr.po | 22 +++++++++++++--------- po/nb_NO.po | 22 +++++++++++++--------- po/nl.po | 22 +++++++++++++--------- po/pa.po | 22 +++++++++++++--------- po/pl.po | 22 +++++++++++++--------- po/pt.po | 22 +++++++++++++--------- po/pt_BR.po | 22 +++++++++++++--------- po/ru.po | 22 +++++++++++++--------- po/si.po | 22 +++++++++++++--------- po/sl.po | 22 +++++++++++++--------- po/sr.po | 22 +++++++++++++--------- po/sv.po | 22 +++++++++++++--------- po/te.po | 22 +++++++++++++--------- po/th.po | 22 +++++++++++++--------- po/tr.po | 22 +++++++++++++--------- po/tzm.po | 22 +++++++++++++--------- po/ug.po | 22 +++++++++++++--------- po/uk.po | 22 +++++++++++++--------- po/zh_Hans.po | 22 +++++++++++++--------- po/zh_Hant.po | 22 +++++++++++++--------- 39 files changed, 508 insertions(+), 351 deletions(-) diff --git a/po/ar.po b/po/ar.po index 41bc10ba4e75..2d2e4d1ce3bb 100644 --- a/po/ar.po +++ b/po/ar.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: lxd\n" "Report-Msgid-Bugs-To: lxd@lists.canonical.com\n" -"POT-Creation-Date: 2024-10-16 10:01+0000\n" +"POT-Creation-Date: 2024-10-18 09:58+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -1692,7 +1692,7 @@ msgstr "" #: lxc/network_acl.go:267 lxc/network_acl.go:328 lxc/network_acl.go:417 #: lxc/network_acl.go:497 lxc/network_acl.go:527 lxc/network_acl.go:658 #: lxc/network_acl.go:707 lxc/network_acl.go:756 lxc/network_acl.go:771 -#: lxc/network_acl.go:892 lxc/network_allocations.go:51 +#: lxc/network_acl.go:892 lxc/network_allocations.go:53 #: lxc/network_forward.go:33 lxc/network_forward.go:90 #: lxc/network_forward.go:171 lxc/network_forward.go:236 #: lxc/network_forward.go:384 lxc/network_forward.go:453 @@ -2417,7 +2417,7 @@ msgstr "" #: lxc/cluster.go:125 lxc/cluster.go:882 lxc/cluster_group.go:406 #: lxc/config_template.go:243 lxc/config_trust.go:352 lxc/config_trust.go:434 #: lxc/image.go:1064 lxc/image_alias.go:157 lxc/list.go:133 lxc/network.go:917 -#: lxc/network.go:1008 lxc/network_acl.go:98 lxc/network_allocations.go:57 +#: lxc/network.go:1008 lxc/network_acl.go:98 lxc/network_allocations.go:59 #: lxc/network_forward.go:93 lxc/network_load_balancer.go:97 #: lxc/network_peer.go:85 lxc/network_zone.go:89 lxc/network_zone.go:697 #: lxc/operation.go:109 lxc/profile.go:638 lxc/project.go:436 @@ -2639,7 +2639,7 @@ msgstr "" msgid "Group ID to run the command as (default 0)" msgstr "" -#: lxc/network_allocations.go:28 +#: lxc/network_allocations.go:29 msgid "HARDWARE ADDRESS" msgstr "" @@ -3308,7 +3308,7 @@ msgid "" " Defaults to -1 (unlimited). Use 0 to limit to the column header size." msgstr "" -#: lxc/network_allocations.go:50 lxc/network_allocations.go:51 +#: lxc/network_allocations.go:52 lxc/network_allocations.go:53 msgid "List network allocations in use" msgstr "" @@ -4012,10 +4012,14 @@ msgstr "" msgid "NAME" msgstr "" -#: lxc/network_allocations.go:27 +#: lxc/network_allocations.go:28 msgid "NAT" msgstr "" +#: lxc/network_allocations.go:26 +msgid "NETWORK" +msgstr "" + #: lxc/project.go:528 msgid "NETWORK ZONES" msgstr "" @@ -4974,7 +4978,7 @@ msgstr "" msgid "Role (admin or read-only)" msgstr "" -#: lxc/network_allocations.go:58 +#: lxc/network_allocations.go:60 msgid "Run again a specific project" msgstr "" @@ -4982,7 +4986,7 @@ msgstr "" msgid "Run against all instances" msgstr "" -#: lxc/network_allocations.go:59 +#: lxc/network_allocations.go:61 msgid "Run against all projects" msgstr "" @@ -5750,7 +5754,7 @@ msgstr "" #: lxc/auth.go:815 lxc/config_trust.go:408 lxc/image.go:1081 #: lxc/image_alias.go:236 lxc/list.go:571 lxc/network.go:982 -#: lxc/network.go:1056 lxc/network_allocations.go:26 lxc/operation.go:172 +#: lxc/network.go:1056 lxc/network_allocations.go:27 lxc/operation.go:172 #: lxc/storage_volume.go:1583 lxc/warning.go:216 msgid "TYPE" msgstr "" diff --git a/po/ber.po b/po/ber.po index 54c35aa642c3..236a29edd22d 100644 --- a/po/ber.po +++ b/po/ber.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: lxd\n" "Report-Msgid-Bugs-To: lxd@lists.canonical.com\n" -"POT-Creation-Date: 2024-10-16 10:01+0000\n" +"POT-Creation-Date: 2024-10-18 09:58+0100\n" "PO-Revision-Date: 2022-03-10 15:10+0000\n" "Last-Translator: Anonymous \n" "Language-Team: Berber \n" "Language-Team: Bulgarian \n" "Language-Team: Catalan \n" "Language-Team: Czech \n" "Language-Team: German \n" "Language-Team: Greek \n" "Language-Team: Esperanto \n" "Language-Team: Spanish \n" "Language-Team: Persian \n" "Language-Team: Finnish \n" "Language-Team: French \n" "Language-Team: Hebrew \n" "Language-Team: Hindi \n" "Language-Team: Indonesian \n" "Language-Team: Italian \n" "Language-Team: Japanese \n" "Language-Team: Korean \n" "Language-Team: Marathi \n" "Language-Team: Norwegian Bokmål \n" "Language-Team: Dutch \n" "Language-Team: Punjabi \n" "Language-Team: Polish \n" "Language-Team: Portuguese (Brazil) \n" "Language-Team: Russian \n" "Language-Team: Sinhala \n" "Language-Team: Slovenian \n" "Language-Team: Serbian \n" "Language-Team: Swedish \n" "Language-Team: Telugu \n" "Language-Team: Turkish \n" "Language-Team: Tamazight (Central Atlas) \n" "Language-Team: Uyghur \n" "Language-Team: Ukrainian \n" "Language-Team: Chinese (Simplified) \n" "Language-Team: Chinese (Traditional)