[lxc-devel] [lxd/master] lxd/backups: Make compression configurable

stgraber on Github lxc-bot at linuxcontainers.org
Fri Aug 24 04:08:00 UTC 2018


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 370 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180824/2c0bd509/attachment.bin>
-------------- next part --------------
From da3d4565fd1d38827fb3508ffff3d081459fab36 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Fri, 24 Aug 2018 00:07:01 -0400
Subject: [PATCH] lxd/backups: Make compression configurable
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Closes #4966

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 doc/api-extensions.md   |  4 ++++
 doc/server.md           |  1 +
 lxd/backup.go           | 49 ++++++++++++++++++++++++++++++-----------
 lxd/cluster/config.go   |  1 +
 lxd/container.go        |  9 +++++++-
 lxd/storage.go          |  2 +-
 lxd/storage_btrfs.go    | 48 +++++++++++++++++++++++++++++-----------
 lxd/storage_ceph.go     | 29 ++++++++++++++++++------
 lxd/storage_dir.go      | 26 +++++++++++++++++-----
 lxd/storage_lvm.go      | 26 +++++++++++++++++-----
 lxd/storage_mock.go     |  2 +-
 lxd/storage_zfs.go      | 45 ++++++++++++++++++++++++++++---------
 scripts/bash/lxd-client |  3 ++-
 shared/version/api.go   |  1 +
 test/suites/backup.sh   | 32 +++++++++++++--------------
 15 files changed, 202 insertions(+), 76 deletions(-)

diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index 1a867b13fe..426b388407 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -575,3 +575,7 @@ This effectively allows for "lxc list" to get all it needs in one query.
 ## candid\_authentication
 This introduces the new candid.api.url config option and removes
 core.macaroon.endpoint.
+
+## backup\_compression
+This introduces a new backups.compression\_algorithm config key which
+allows configuration of backup compression.
diff --git a/doc/server.md b/doc/server.md
index 7216031d8e..afda09b248 100644
--- a/doc/server.md
+++ b/doc/server.md
@@ -10,6 +10,7 @@ currently supported:
 
 Key                             | Type      | Default   | API extension            | Description
 :--                             | :---      | :------   | :------------            | :----------
+backups.compression\_algorithm  | string    | gzip      | backup\_compression      | Compression algorithm to use for new images (bzip2, gzip, lzma, xz or none)
 candid.api.url                  | string    | -         | candid\_authentication   | URL of the the external authentication endpoint using Candid
 cluster.offline\_threshold      | integer   | 20        | clustering               | Number of seconds after which an unresponsive node is considered offline
 core.debug\_address             | string    | -         | pprof\_http              | Address to bind the pprof debug server to (HTTP)
diff --git a/lxd/backup.go b/lxd/backup.go
index d17b256a64..f313b7cd7e 100644
--- a/lxd/backup.go
+++ b/lxd/backup.go
@@ -12,6 +12,7 @@ import (
 
 	"gopkg.in/yaml.v2"
 
+	"github.com/lxc/lxd/lxd/cluster"
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/state"
 	"github.com/lxc/lxd/shared"
@@ -176,7 +177,6 @@ func (b *backup) Render() *api.ContainerBackup {
 }
 
 func backupGetInfo(r io.ReadSeeker) (*backupInfo, error) {
-	var buf bytes.Buffer
 	var tr *tar.Reader
 	result := backupInfo{}
 	hasBinaryFormat := false
@@ -184,13 +184,29 @@ func backupGetInfo(r io.ReadSeeker) (*backupInfo, error) {
 
 	// Extract
 	r.Seek(0, 0)
-
-	err := shared.RunCommandWithFds(r, &buf, "xz", "-d")
+	_, _, unpacker, err := shared.DetectCompressionFile(r)
 	if err != nil {
 		return nil, err
 	}
+	r.Seek(0, 0)
+
+	if unpacker == nil {
+		return nil, fmt.Errorf("Unsupported backup compression")
+	}
+
+	if len(unpacker) > 0 {
+		var buf bytes.Buffer
+
+		err := shared.RunCommandWithFds(r, &buf, unpacker[0], unpacker[1:]...)
+		if err != nil {
+			return nil, err
+		}
+
+		tr = tar.NewReader(&buf)
+	} else {
+		tr = tar.NewReader(r)
+	}
 
-	tr = tar.NewReader(&buf)
 	for {
 		hdr, err := tr.Next()
 		if err == io.EOF {
@@ -288,7 +304,7 @@ func backupFixStoragePool(c *db.Cluster, b backupInfo) error {
 	return nil
 }
 
-func backupCreateTarball(path string, backup backup) error {
+func backupCreateTarball(s *state.State, path string, backup backup) error {
 	container := backup.container
 
 	// Create the index
@@ -351,19 +367,26 @@ func backupCreateTarball(path string, backup backup) error {
 	}
 
 	// Compress it
-	compressedPath, err := compressFile(backupPath, "xz")
+	compress, err := cluster.ConfigGetString(s.Cluster, "backups.compression_algorithm")
 	if err != nil {
 		return err
 	}
 
-	err = os.Remove(backupPath)
-	if err != nil {
-		return err
-	}
+	if compress != "none" {
+		compressedPath, err := compressFile(backupPath, compress)
+		if err != nil {
+			return err
+		}
 
-	err = os.Rename(compressedPath, backupPath)
-	if err != nil {
-		return err
+		err = os.Remove(backupPath)
+		if err != nil {
+			return err
+		}
+
+		err = os.Rename(compressedPath, backupPath)
+		if err != nil {
+			return err
+		}
 	}
 
 	// Set permissions
diff --git a/lxd/cluster/config.go b/lxd/cluster/config.go
index f41dceb3d3..0b12b46ac4 100644
--- a/lxd/cluster/config.go
+++ b/lxd/cluster/config.go
@@ -201,6 +201,7 @@ func configGet(cluster *db.Cluster) (*Config, error) {
 
 // ConfigSchema defines available server configuration keys.
 var ConfigSchema = config.Schema{
+	"backups.compression_algorithm":  {Default: "gzip", Validator: validateCompression},
 	"cluster.offline_threshold":      {Type: config.Int64, Default: offlineThresholdDefault(), Validator: offlineThresholdValidator},
 	"core.https_allowed_headers":     {},
 	"core.https_allowed_methods":     {},
diff --git a/lxd/container.go b/lxd/container.go
index 40956b360c..a6136eb16b 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -707,8 +707,15 @@ func containerCreateFromBackup(s *state.State, info backupInfo, data io.ReadSeek
 		fixBackupFile = true
 	}
 
+	// Find the compression algorithm
+	tarArgs, _, _, err := shared.DetectCompressionFile(data)
+	if err != nil {
+		return err
+	}
+	data.Seek(0, 0)
+
 	// Unpack tarball
-	err := pool.ContainerBackupLoad(info, data)
+	err = pool.ContainerBackupLoad(info, data, tarArgs)
 	if err != nil {
 		return err
 	}
diff --git a/lxd/storage.go b/lxd/storage.go
index 6d6b98baac..1785ce5a3a 100644
--- a/lxd/storage.go
+++ b/lxd/storage.go
@@ -188,7 +188,7 @@ type storage interface {
 	ContainerSnapshotStop(c container) (bool, error)
 
 	ContainerBackupCreate(backup backup, sourceContainer container) error
-	ContainerBackupLoad(info backupInfo, data io.ReadSeeker) error
+	ContainerBackupLoad(info backupInfo, data io.ReadSeeker, tarArgs []string) error
 
 	// For use in migrating snapshots.
 	ContainerSnapshotCreateEmpty(c container) error
diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go
index a39b4cb21f..1559321483 100644
--- a/lxd/storage_btrfs.go
+++ b/lxd/storage_btrfs.go
@@ -1692,7 +1692,7 @@ func (s *storageBtrfs) ContainerBackupCreate(backup backup, source container) er
 	}
 
 	// Pack the backup
-	err = backupCreateTarball(tmpPath, backup)
+	err = backupCreateTarball(s.s, tmpPath, backup)
 	if err != nil {
 		return err
 	}
@@ -1700,7 +1700,7 @@ func (s *storageBtrfs) ContainerBackupCreate(backup backup, source container) er
 	return nil
 }
 
-func (s *storageBtrfs) doContainerBackupLoadOptimized(info backupInfo, data io.ReadSeeker) error {
+func (s *storageBtrfs) doContainerBackupLoadOptimized(info backupInfo, data io.ReadSeeker, tarArgs []string) error {
 	containerName, _, _ := containerGetParentAndSnapshotName(info.Name)
 
 	containerMntPoint := getContainerMountPoint(s.pool.Name, "")
@@ -1721,10 +1721,16 @@ func (s *storageBtrfs) doContainerBackupLoadOptimized(info backupInfo, data io.R
 		return err
 	}
 
+	// Prepare tar arguments
+	args := append(tarArgs, []string{
+		"-",
+		"--strip-components=1",
+		"-C", unpackPath, "backup",
+	}...)
+
 	// Extract container
 	data.Seek(0, 0)
-	err = shared.RunCommandWithFds(data, nil, "tar", "-xJf", "-",
-		"--strip-components=1", "-C", unpackPath, "backup")
+	err = shared.RunCommandWithFds(data, nil, "tar", args...)
 	if err != nil {
 		logger.Errorf("Failed to untar \"%s\" into \"%s\": %s", "backup", unpackPath, err)
 		return err
@@ -1792,7 +1798,7 @@ func (s *storageBtrfs) doContainerBackupLoadOptimized(info backupInfo, data io.R
 	return nil
 }
 
-func (s *storageBtrfs) doContainerBackupLoadVanilla(info backupInfo, data io.ReadSeeker) error {
+func (s *storageBtrfs) doContainerBackupLoadVanilla(info backupInfo, data io.ReadSeeker, tarArgs []string) error {
 	// create the main container
 	err := s.doContainerCreate(info.Name, info.Privileged)
 	if err != nil {
@@ -1802,11 +1808,20 @@ func (s *storageBtrfs) doContainerBackupLoadVanilla(info backupInfo, data io.Rea
 	containerMntPoint := getContainerMountPoint(s.pool.Name, info.Name)
 	// Extract container
 	for _, snap := range info.Snapshots {
-		// Extract snapshots
 		cur := fmt.Sprintf("backup/snapshots/%s", snap)
+
+		// Prepare tar arguments
+		args := append(tarArgs, []string{
+			"-",
+			"--recursive-unlink",
+			"--xattrs-include=*",
+			"--strip-components=3",
+			"-C", containerMntPoint, cur,
+		}...)
+
+		// Extract snapshots
 		data.Seek(0, 0)
-		err = shared.RunCommandWithFds(data, nil, "tar", "-xJf", "-",
-			"--recursive-unlink", "--xattrs-include=*", "--strip-components=3", "-C", containerMntPoint, cur)
+		err = shared.RunCommandWithFds(data, nil, "tar", args...)
 		if err != nil {
 			logger.Errorf("Failed to untar \"%s\" into \"%s\": %s", cur, containerMntPoint, err)
 			return err
@@ -1819,10 +1834,17 @@ func (s *storageBtrfs) doContainerBackupLoadVanilla(info backupInfo, data io.Rea
 		}
 	}
 
+	// Prepare tar arguments
+	args := append(tarArgs, []string{
+		"-",
+		"--strip-components=2",
+		"--xattrs-include=*",
+		"-C", containerMntPoint, "backup/container",
+	}...)
+
 	// Extract container
 	data.Seek(0, 0)
-	err = shared.RunCommandWithFds(data, nil, "tar", "-xJf", "-",
-		"--strip-components=2", "--xattrs-include=*", "-C", containerMntPoint, "backup/container")
+	err = shared.RunCommandWithFds(data, nil, "tar", args...)
 	if err != nil {
 		logger.Errorf("Failed to untar \"backup/container\" into \"%s\": %s", containerMntPoint, err)
 		return err
@@ -1831,14 +1853,14 @@ func (s *storageBtrfs) doContainerBackupLoadVanilla(info backupInfo, data io.Rea
 	return nil
 }
 
-func (s *storageBtrfs) ContainerBackupLoad(info backupInfo, data io.ReadSeeker) error {
+func (s *storageBtrfs) ContainerBackupLoad(info backupInfo, data io.ReadSeeker, tarArgs []string) error {
 	logger.Debugf("Loading BTRFS storage volume for backup \"%s\" on storage pool \"%s\"", info.Name, s.pool.Name)
 
 	if info.HasBinaryFormat {
-		return s.doContainerBackupLoadOptimized(info, data)
+		return s.doContainerBackupLoadOptimized(info, data, tarArgs)
 	}
 
-	return s.doContainerBackupLoadVanilla(info, data)
+	return s.doContainerBackupLoadVanilla(info, data, tarArgs)
 }
 
 func (s *storageBtrfs) ImageCreate(fingerprint string) error {
diff --git a/lxd/storage_ceph.go b/lxd/storage_ceph.go
index 2dd3ccd0af..f34ca55409 100644
--- a/lxd/storage_ceph.go
+++ b/lxd/storage_ceph.go
@@ -1918,7 +1918,7 @@ func (s *storageCeph) ContainerBackupCreate(backup backup, source container) err
 	}
 
 	// Pack the backup
-	err = backupCreateTarball(tmpPath, backup)
+	err = backupCreateTarball(s.s, tmpPath, backup)
 	if err != nil {
 		return err
 	}
@@ -1932,7 +1932,7 @@ func (s *storageCeph) ContainerBackupCreate(backup backup, source container) err
 // - for each snapshot dump the contents into the empty storage volume and
 //   after each dump take a snapshot of the rbd storage volume
 // - dump the container contents into the rbd storage volume.
-func (s *storageCeph) ContainerBackupLoad(info backupInfo, data io.ReadSeeker) error {
+func (s *storageCeph) ContainerBackupLoad(info backupInfo, data io.ReadSeeker, tarArgs []string) error {
 	// create the main container
 	err := s.doContainerCreate(info.Name, info.Privileged)
 	if err != nil {
@@ -1948,12 +1948,20 @@ func (s *storageCeph) ContainerBackupLoad(info backupInfo, data io.ReadSeeker) e
 	containerMntPoint := getContainerMountPoint(s.pool.Name, info.Name)
 	// Extract container
 	for _, snap := range info.Snapshots {
-		// Extract snapshots
 		cur := fmt.Sprintf("backup/snapshots/%s", snap)
 
+		// Prepare tar arguments
+		args := append(tarArgs, []string{
+			"-",
+			"--recursive-unlink",
+			"--strip-components=3",
+			"--xattrs-include=*",
+			"-C", containerMntPoint, cur,
+		}...)
+
+		// Extract snapshots
 		data.Seek(0, 0)
-		err = shared.RunCommandWithFds(data, nil, "tar", "-xJf", "-",
-			"--recursive-unlink", "--strip-components=3", "--xattrs-include=*", "-C", containerMntPoint, cur)
+		err = shared.RunCommandWithFds(data, nil, "tar", args...)
 		if err != nil {
 			logger.Errorf("Failed to untar \"%s\" into \"%s\": %s", cur, containerMntPoint, err)
 			return err
@@ -1979,10 +1987,17 @@ func (s *storageCeph) ContainerBackupLoad(info backupInfo, data io.ReadSeeker) e
 		}
 	}
 
+	// Prepare tar arguments
+	args := append(tarArgs, []string{
+		"-",
+		"--strip-components=2",
+		"--xattrs-include=*",
+		"-C", containerMntPoint, "backup/container",
+	}...)
+
 	// Extract container
 	data.Seek(0, 0)
-	err = shared.RunCommandWithFds(data, nil, "tar", "-xJf", "-",
-		"--strip-components=2", "--xattrs-include=*", "-C", containerMntPoint, "backup/container")
+	err = shared.RunCommandWithFds(data, nil, "tar", args...)
 	if err != nil {
 		logger.Errorf("Failed to untar \"backup/container\" into \"%s\": %s", containerMntPoint, err)
 		return err
diff --git a/lxd/storage_dir.go b/lxd/storage_dir.go
index ba63a7597d..0e2fa6efae 100644
--- a/lxd/storage_dir.go
+++ b/lxd/storage_dir.go
@@ -1113,7 +1113,7 @@ func (s *storageDir) ContainerBackupCreate(backup backup, source container) erro
 	}
 
 	// Pack the backup
-	err = backupCreateTarball(tmpPath, backup)
+	err = backupCreateTarball(s.s, tmpPath, backup)
 	if err != nil {
 		return err
 	}
@@ -1121,7 +1121,7 @@ func (s *storageDir) ContainerBackupCreate(backup backup, source container) erro
 	return nil
 }
 
-func (s *storageDir) ContainerBackupLoad(info backupInfo, data io.ReadSeeker) error {
+func (s *storageDir) ContainerBackupLoad(info backupInfo, data io.ReadSeeker, tarArgs []string) error {
 	_, err := s.StoragePoolMount()
 	if err != nil {
 		return err
@@ -1140,10 +1140,17 @@ func (s *storageDir) ContainerBackupLoad(info backupInfo, data io.ReadSeeker) er
 		return err
 	}
 
+	// Prepare tar arguments
+	args := append(tarArgs, []string{
+		"-",
+		"--strip-components=2",
+		"--xattrs-include=*",
+		"-C", containerMntPoint, "backup/container",
+	}...)
+
 	// Extract container
 	data.Seek(0, 0)
-	err = shared.RunCommandWithFds(data, nil, "tar", "-xJf",
-		"-", "--strip-components=2", "--xattrs-include=*", "-C", containerMntPoint, "backup/container")
+	err = shared.RunCommandWithFds(data, nil, "tar", args...)
 	if err != nil {
 		return err
 	}
@@ -1160,10 +1167,17 @@ func (s *storageDir) ContainerBackupLoad(info backupInfo, data io.ReadSeeker) er
 			return err
 		}
 
+		// Prepare tar arguments
+		args := append(tarArgs, []string{
+			"-",
+			"--strip-components=2",
+			"--xattrs-include=*",
+			"-C", containerMntPoint, "backup/snapshots",
+		}...)
+
 		// Extract snapshots
 		data.Seek(0, 0)
-		err = shared.RunCommandWithFds(data, nil, "tar", "-xJf", "-",
-			"--strip-components=2", "--xattrs-include=*", "-C", snapshotMntPoint, "backup/snapshots")
+		err = shared.RunCommandWithFds(data, nil, "tar", args...)
 		if err != nil {
 			return err
 		}
diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go
index 036761210e..18e986b0db 100644
--- a/lxd/storage_lvm.go
+++ b/lxd/storage_lvm.go
@@ -1679,7 +1679,7 @@ func (s *storageLvm) ContainerBackupCreate(backup backup, source container) erro
 	}
 
 	// Pack the backup
-	err = backupCreateTarball(tmpPath, backup)
+	err = backupCreateTarball(s.s, tmpPath, backup)
 	if err != nil {
 		return err
 	}
@@ -1687,16 +1687,23 @@ func (s *storageLvm) ContainerBackupCreate(backup backup, source container) erro
 	return nil
 }
 
-func (s *storageLvm) ContainerBackupLoad(info backupInfo, data io.ReadSeeker) error {
+func (s *storageLvm) ContainerBackupLoad(info backupInfo, data io.ReadSeeker, tarArgs []string) error {
 	containerPath, err := s.doContainerBackupLoad(info.Name, info.Privileged, false)
 	if err != nil {
 		return err
 	}
 
+	// Prepare tar arguments
+	args := append(tarArgs, []string{
+		"-",
+		"--strip-components=2",
+		"--xattrs-include=*",
+		"-C", containerPath, "backup/container",
+	}...)
+
 	// Extract container
 	data.Seek(0, 0)
-	err = shared.RunCommandWithFds(data, nil, "tar", "-xJf", "-", "--strip-components=2", "--xattrs-include=*",
-		"-C", containerPath, "backup/container")
+	err = shared.RunCommandWithFds(data, nil, "tar", args...)
 	if err != nil {
 		return err
 	}
@@ -1708,10 +1715,17 @@ func (s *storageLvm) ContainerBackupLoad(info backupInfo, data io.ReadSeeker) er
 			return err
 		}
 
+		// Prepare tar arguments
+		args := append(tarArgs, []string{
+			"-",
+			"--strip-components=3",
+			"--xattrs-include=*",
+			"-C", containerPath, fmt.Sprintf("backup/snapshots/%s", snap),
+		}...)
+
 		// Extract snapshots
 		data.Seek(0, 0)
-		err = shared.RunCommandWithFds(data, nil, "tar", "-xJf", "-",
-			"--strip-components=3", "--xattrs-include=*", "-C", containerPath, fmt.Sprintf("backup/snapshots/%s", snap))
+		err = shared.RunCommandWithFds(data, nil, "tar", args...)
 		if err != nil {
 			return err
 		}
diff --git a/lxd/storage_mock.go b/lxd/storage_mock.go
index 201ea1d065..11e60b1212 100644
--- a/lxd/storage_mock.go
+++ b/lxd/storage_mock.go
@@ -194,7 +194,7 @@ func (s *storageMock) ContainerBackupCreate(backup backup, sourceContainer conta
 	return nil
 }
 
-func (s *storageMock) ContainerBackupLoad(info backupInfo, data io.ReadSeeker) error {
+func (s *storageMock) ContainerBackupLoad(info backupInfo, data io.ReadSeeker, tarArgs []string) error {
 	return nil
 }
 
diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go
index d96eaae42e..d7cf6829f9 100644
--- a/lxd/storage_zfs.go
+++ b/lxd/storage_zfs.go
@@ -2044,7 +2044,7 @@ func (s *storageZfs) ContainerBackupCreate(backup backup, source container) erro
 	}
 
 	// Pack the backup
-	err = backupCreateTarball(tmpPath, backup)
+	err = backupCreateTarball(s.s, tmpPath, backup)
 	if err != nil {
 		return err
 	}
@@ -2052,7 +2052,7 @@ func (s *storageZfs) ContainerBackupCreate(backup backup, source container) erro
 	return nil
 }
 
-func (s *storageZfs) doContainerBackupLoadOptimized(info backupInfo, data io.ReadSeeker) error {
+func (s *storageZfs) doContainerBackupLoadOptimized(info backupInfo, data io.ReadSeeker, tarArgs []string) error {
 	containerName, _, _ := containerGetParentAndSnapshotName(info.Name)
 	containerMntPoint := getContainerMountPoint(s.pool.Name, containerName)
 	err := createContainerMountpoint(containerMntPoint, containerPath(info.Name, false), info.Privileged)
@@ -2073,9 +2073,16 @@ func (s *storageZfs) doContainerBackupLoadOptimized(info backupInfo, data io.Rea
 		return err
 	}
 
+	// Prepare tar arguments
+	args := append(tarArgs, []string{
+		"-",
+		"--strip-components=1",
+		"-C", unpackPath, "backup",
+	}...)
+
 	// Extract container
 	data.Seek(0, 0)
-	err = shared.RunCommandWithFds(data, nil, "tar", "-xJf", "-", "--strip-components=1", "-C", unpackPath, "backup")
+	err = shared.RunCommandWithFds(data, nil, "tar", args...)
 	if err != nil {
 		// can't use defer because it needs to run before the mount
 		os.RemoveAll(unpackPath)
@@ -2155,7 +2162,7 @@ func (s *storageZfs) doContainerBackupLoadOptimized(info backupInfo, data io.Rea
 	return nil
 }
 
-func (s *storageZfs) doContainerBackupLoadVanilla(info backupInfo, data io.ReadSeeker) error {
+func (s *storageZfs) doContainerBackupLoadVanilla(info backupInfo, data io.ReadSeeker, tarArgs []string) error {
 	// create the main container
 	err := s.doContainerCreate(info.Name, info.Privileged)
 	if err != nil {
@@ -2174,9 +2181,18 @@ func (s *storageZfs) doContainerBackupLoadVanilla(info backupInfo, data io.ReadS
 		// Extract snapshots
 		cur := fmt.Sprintf("backup/snapshots/%s", snap)
 
+		// Prepare tar arguments
+		args := append(tarArgs, []string{
+			"-",
+			"--recursive-unlink",
+			"--strip-components=3",
+			"--xattrs-include=*",
+			"-C", containerMntPoint, cur,
+		}...)
+
+		// Unpack
 		data.Seek(0, 0)
-		err = shared.RunCommandWithFds(data, nil, "tar", "-xJf", "-",
-			"--recursive-unlink", "--strip-components=3", "--xattrs-include=*", "-C", containerMntPoint, cur)
+		err = shared.RunCommandWithFds(data, nil, "tar", args...)
 		if err != nil {
 			logger.Errorf("Failed to untar \"%s\" into \"%s\": %s", cur, containerMntPoint, err)
 			return err
@@ -2189,10 +2205,17 @@ func (s *storageZfs) doContainerBackupLoadVanilla(info backupInfo, data io.ReadS
 		}
 	}
 
+	// Prepare tar arguments
+	args := append(tarArgs, []string{
+		"-",
+		"--strip-components=2",
+		"--xattrs-include=*",
+		"-C", containerMntPoint, "backup/container",
+	}...)
+
 	// Extract container
 	data.Seek(0, 0)
-	err = shared.RunCommandWithFds(data, nil, "tar", "-xJf", "-",
-		"--strip-components=2", "--xattrs-include=*", "-C", containerMntPoint, "backup/container")
+	err = shared.RunCommandWithFds(data, nil, "tar", args...)
 	if err != nil {
 		logger.Errorf("Failed to untar \"backup/container\" into \"%s\": %s", containerMntPoint, err)
 		return err
@@ -2201,14 +2224,14 @@ func (s *storageZfs) doContainerBackupLoadVanilla(info backupInfo, data io.ReadS
 	return nil
 }
 
-func (s *storageZfs) ContainerBackupLoad(info backupInfo, data io.ReadSeeker) error {
+func (s *storageZfs) ContainerBackupLoad(info backupInfo, data io.ReadSeeker, tarArgs []string) error {
 	logger.Debugf("Loading ZFS storage volume for backup \"%s\" on storage pool \"%s\"", info.Name, s.pool.Name)
 
 	if info.HasBinaryFormat {
-		return s.doContainerBackupLoadOptimized(info, data)
+		return s.doContainerBackupLoadOptimized(info, data, tarArgs)
 	}
 
-	return s.doContainerBackupLoadVanilla(info, data)
+	return s.doContainerBackupLoadVanilla(info, data, tarArgs)
 }
 
 // - create temporary directory ${LXD_DIR}/images/lxd_images_
diff --git a/scripts/bash/lxd-client b/scripts/bash/lxd-client
index 0657c74bcf..8003717efc 100644
--- a/scripts/bash/lxd-client
+++ b/scripts/bash/lxd-client
@@ -64,7 +64,8 @@ _have lxc && {
       list manpage monitor move network profile publish query remote rename \
       restart restore shell snapshot start stop storage version"
 
-    global_keys="core.https_address core.https_allowed_credentials \
+    global_keys="backups.compression_algorithm,
+      core.https_address core.https_allowed_credentials \
       core.https_allowed_headers core.https_allowed_methods \
       core.https_allowed_origin candid.api.url core.proxy_https \
       core.proxy_http core.proxy_ignore_hosts core.trust_password \
diff --git a/shared/version/api.go b/shared/version/api.go
index a81f75099e..0ac1c116c7 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -121,6 +121,7 @@ var APIExtensions = []string{
 	"network_nat_order",
 	"container_full",
 	"candid_authentication",
+	"backup_compression",
 }
 
 // APIExtensionsCount returns the number of available API extensions.
diff --git a/test/suites/backup.sh b/test/suites/backup.sh
index d11b90fdc6..3eb89f8999 100644
--- a/test/suites/backup.sh
+++ b/test/suites/backup.sh
@@ -203,20 +203,20 @@ test_backup_import() {
 
   # create backup
   if [ "$lxd_backend" = "btrfs" ] || [ "$lxd_backend" = "zfs" ]; then
-    lxc export b1 "${LXD_DIR}/c1-optimized.tar.xz" --optimized-storage --container-only
+    lxc export b1 "${LXD_DIR}/c1-optimized.tar.gz" --optimized-storage --container-only
   fi
 
-  lxc export b1 "${LXD_DIR}/c1.tar.xz" --container-only
+  lxc export b1 "${LXD_DIR}/c1.tar.gz" --container-only
   lxc delete --force b1
 
   # import backup, and ensure it's valid and runnable
-  lxc import "${LXD_DIR}/c1.tar.xz"
+  lxc import "${LXD_DIR}/c1.tar.gz"
   lxc info b1
   lxc start b1
   lxc delete --force b1
 
   if [ "$lxd_backend" = "btrfs" ] || [ "$lxd_backend" = "zfs" ]; then
-    lxc import "${LXD_DIR}/c1-optimized.tar.xz"
+    lxc import "${LXD_DIR}/c1-optimized.tar.gz"
     lxc info b1
     lxc start b1
     lxc delete --force b1
@@ -225,13 +225,13 @@ test_backup_import() {
   # with snapshots
 
   if [ "$lxd_backend" = "btrfs" ] || [ "$lxd_backend" = "zfs" ]; then
-    lxc export b2 "${LXD_DIR}/c2-optimized.tar.xz" --optimized-storage
+    lxc export b2 "${LXD_DIR}/c2-optimized.tar.gz" --optimized-storage
   fi
 
-  lxc export b2 "${LXD_DIR}/c2.tar.xz"
+  lxc export b2 "${LXD_DIR}/c2.tar.gz"
   lxc delete --force b2
 
-  lxc import "${LXD_DIR}/c2.tar.xz"
+  lxc import "${LXD_DIR}/c2.tar.gz"
   lxc info b2 | grep snap0
   lxc start b2
   lxc stop b2 --force
@@ -241,7 +241,7 @@ test_backup_import() {
   lxc delete --force b2
 
   if [ "$lxd_backend" = "btrfs" ] || [ "$lxd_backend" = "zfs" ]; then
-    lxc import "${LXD_DIR}/c2-optimized.tar.xz"
+    lxc import "${LXD_DIR}/c2-optimized.tar.gz"
     lxc info b2 | grep snap0
     lxc start b2
     lxc stop b2 --force
@@ -265,16 +265,16 @@ test_backup_export() {
   # container only
 
   if [ "$lxd_backend" = "btrfs" ] || [ "$lxd_backend" = "zfs" ]; then
-    lxc export b1 "${LXD_DIR}/c1-optimized.tar.xz" --optimized-storage --container-only
-    tar -xJf "${LXD_DIR}/c1-optimized.tar.xz" -C "${LXD_DIR}/optimized"
+    lxc export b1 "${LXD_DIR}/c1-optimized.tar.gz" --optimized-storage --container-only
+    tar -xzf "${LXD_DIR}/c1-optimized.tar.gz" -C "${LXD_DIR}/optimized"
 
     [ -f "${LXD_DIR}/optimized/backup/index.yaml" ]
     [ -f "${LXD_DIR}/optimized/backup/container.bin" ]
     [ ! -d "${LXD_DIR}/optimized/backup/snapshots" ]
   fi
 
-  lxc export b1 "${LXD_DIR}/c1.tar.xz" --container-only
-  tar -xJf "${LXD_DIR}/c1.tar.xz" -C "${LXD_DIR}/non-optimized"
+  lxc export b1 "${LXD_DIR}/c1.tar.gz" --container-only
+  tar -xzf "${LXD_DIR}/c1.tar.gz" -C "${LXD_DIR}/non-optimized"
 
   # check tarball content
   [ -f "${LXD_DIR}/non-optimized/backup/index.yaml" ]
@@ -286,16 +286,16 @@ test_backup_export() {
   # with snapshots
 
   if [ "$lxd_backend" = "btrfs" ] || [ "$lxd_backend" = "zfs" ]; then
-    lxc export b1 "${LXD_DIR}/c2-optimized.tar.xz" --optimized-storage
-    tar -xJf "${LXD_DIR}/c2-optimized.tar.xz" -C "${LXD_DIR}/optimized"
+    lxc export b1 "${LXD_DIR}/c2-optimized.tar.gz" --optimized-storage
+    tar -xzf "${LXD_DIR}/c2-optimized.tar.gz" -C "${LXD_DIR}/optimized"
 
     [ -f "${LXD_DIR}/optimized/backup/index.yaml" ]
     [ -f "${LXD_DIR}/optimized/backup/container.bin" ]
     [ -f "${LXD_DIR}/optimized/backup/snapshots/snap0.bin" ]
   fi
 
-  lxc export b1 "${LXD_DIR}/c2.tar.xz"
-  tar -xJf "${LXD_DIR}/c2.tar.xz" -C "${LXD_DIR}/non-optimized"
+  lxc export b1 "${LXD_DIR}/c2.tar.gz"
+  tar -xzf "${LXD_DIR}/c2.tar.gz" -C "${LXD_DIR}/non-optimized"
 
   # check tarball content
   [ -f "${LXD_DIR}/non-optimized/backup/index.yaml" ]


More information about the lxc-devel mailing list