The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/3185
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === Signed-off-by: Christian Brauner <[email protected]>
From d8b8ee7e45a3c52fe6d3b3cd03c1d122ccd5b566 Mon Sep 17 00:00:00 2001 From: Christian Brauner <[email protected]> Date: Tue, 18 Apr 2017 15:12:36 +0200 Subject: [PATCH 1/2] storage pools: implement rsync.bwlimit property When rsync has to be invoked to transfer storage entities (e.g. on local or remote migration) there is currently no way to place an upper limit on the amount of socket I/O allowed. This can lead to problems on systems with limited ram available. Starting with this commit LXD allows setting rsync.bwlimit to place an upper limit on the amount of socket I/O allowed. This property is storage pool specific rather than a global daemon config key. In this way, users can grant important pools more I/O than less important pools. Closes #2678. Signed-off-by: Christian Brauner <[email protected]> --- doc/configuration.md | 1 + doc/storage-backends.md | 3 +++ lxd/api_1.0.go | 1 + lxd/db_update.go | 2 +- lxd/migrate.go | 13 ++++++++++--- lxd/patches.go | 12 ++++++------ lxd/rsync.go | 46 +++++++++++++++++++++++++++++++++++++++------ lxd/storage.go | 27 -------------------------- lxd/storage_btrfs.go | 13 +++++++++---- lxd/storage_dir.go | 24 +++++++++++++++-------- lxd/storage_lvm.go | 17 +++++++++++------ lxd/storage_migration.go | 15 ++++++++------- lxd/storage_pools_config.go | 9 +++++++++ lxd/storage_zfs.go | 9 ++++++--- 14 files changed, 121 insertions(+), 71 deletions(-) diff --git a/doc/configuration.md b/doc/configuration.md index ff69154..a95e46c 100644 --- a/doc/configuration.md +++ b/doc/configuration.md @@ -398,6 +398,7 @@ volume.block.mount\_options | string | block based driver (lvm) lvm.thinpool\_name | string | lvm driver | LXDPool | Thin pool where images and containers are created. lvm.use\_thinpool | bool | lvm driver | true | Whether the storage pool uses a thinpool for logical volumes. lvm.vg\_name | string | lvm driver | name of the pool | Name of the volume group to create. +rsync.bwlimit | string | - | 0 (no limit) | Specifies the upper limit to be placed on the socket I/O whenever rsync has to be used to transfer storage entities. volume.size | string | appropriate driver | 0 | Default volume size volume.zfs.remove\_snapshots | bool | zfs driver | false | Remove snapshots as needed volume.zfs.use\_refquota | bool | zfs driver | false | Use refquota instead of quota for space. diff --git a/doc/storage-backends.md b/doc/storage-backends.md index e5f3d4b..58ae60d 100644 --- a/doc/storage-backends.md +++ b/doc/storage-backends.md @@ -43,6 +43,9 @@ LXD uses those features to transfer containers and snapshots between servers. When such capabilities aren't available, either because the storage driver doesn't support it or because the storage backend of the source and target servers differ, LXD will fallback to using rsync to transfer the individual files instead. +When rsync has to be used LXD allows to specify an upper limit on the amount of +socket I/O by setting the "rsync.bwlimit" storage pool property to a non-zero +value. ## Default storage pool There is no concept of a default storage pool in LXD. diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go index dff9690..7cd7b5b 100644 --- a/lxd/api_1.0.go +++ b/lxd/api_1.0.go @@ -102,6 +102,7 @@ func api10Get(d *Daemon, r *http.Request) Response { "storage_zfs_clone_copy", "unix_device_rename", "storage_lvm_use_thinpool", + "storage_rsync_bwlimit", }, APIStatus: "stable", APIVersion: version.APIVersion, diff --git a/lxd/db_update.go b/lxd/db_update.go index f40208b..bc641c6 100644 --- a/lxd/db_update.go +++ b/lxd/db_update.go @@ -576,7 +576,7 @@ func dbUpdateFromV11(currentVersion int, version int, d *Daemon) error { // containers/<container>/snapshots/<snap0> // to // snapshots/<container>/<snap0> - output, err := storageRsyncCopy(oldPath, newPath) + output, err := rsyncLocalCopy(oldPath, newPath, "") if err != nil { logger.Error( "Failed rsync snapshot", diff --git a/lxd/migrate.go b/lxd/migrate.go index 77ea9d3..1a8ad44 100644 --- a/lxd/migrate.go +++ b/lxd/migrate.go @@ -374,11 +374,18 @@ func (s *migrationSourceWs) Do(migrateOp *operation) error { return err } + bwlimit := "" if *header.Fs != myType { myType = MigrationFSType_RSYNC header.Fs = &myType driver, _ = rsyncMigrationSource(s.container, s.containerOnly) + + // Check if this storage pool has a rate limit set for rsync. + poolwritable := s.container.Storage().GetStoragePoolWritable() + if poolwritable.Config != nil { + bwlimit = poolwritable.Config["rsync.bwlimit"] + } } // All failure paths need to do a few things to correctly handle errors before returning. @@ -394,7 +401,7 @@ func (s *migrationSourceWs) Do(migrateOp *operation) error { return err } - err = driver.SendWhileRunning(s.fsConn, migrateOp) + err = driver.SendWhileRunning(s.fsConn, migrateOp, bwlimit) if err != nil { return abort(err) } @@ -515,12 +522,12 @@ func (s *migrationSourceWs) Do(migrateOp *operation) error { * no reason to do these in parallel. In the future when we're using * p.haul's protocol, it will make sense to do these in parallel. */ - err = RsyncSend(shared.AddSlash(checkpointDir), s.criuConn, nil) + err = RsyncSend(shared.AddSlash(checkpointDir), s.criuConn, nil, bwlimit) if err != nil { return abort(err) } - err = driver.SendAfterCheckpoint(s.fsConn) + err = driver.SendAfterCheckpoint(s.fsConn, bwlimit) if err != nil { return abort(err) } diff --git a/lxd/patches.go b/lxd/patches.go index 1123865..83bdd16 100644 --- a/lxd/patches.go +++ b/lxd/patches.go @@ -393,7 +393,7 @@ func upgradeFromStorageTypeBtrfs(name string, d *Daemon, defaultPoolName string, return err } - output, err := storageRsyncCopy(oldContainerMntPoint, newContainerMntPoint) + output, err := rsyncLocalCopy(oldContainerMntPoint, newContainerMntPoint, "") if err != nil { logger.Errorf("Failed to rsync: %s: %s.", output, err) return err @@ -480,7 +480,7 @@ func upgradeFromStorageTypeBtrfs(name string, d *Daemon, defaultPoolName string, return err } - output, err := storageRsyncCopy(oldSnapshotMntPoint, newSnapshotMntPoint) + output, err := rsyncLocalCopy(oldSnapshotMntPoint, newSnapshotMntPoint, "") if err != nil { logger.Errorf("Failed to rsync: %s: %s.", output, err) return err @@ -684,7 +684,7 @@ func upgradeFromStorageTypeDir(name string, d *Daemon, defaultPoolName string, d // First try to rename. err := os.Rename(oldContainerMntPoint, newContainerMntPoint) if err != nil { - output, err := storageRsyncCopy(oldContainerMntPoint, newContainerMntPoint) + output, err := rsyncLocalCopy(oldContainerMntPoint, newContainerMntPoint, "") if err != nil { logger.Errorf("Failed to rsync: %s: %s.", output, err) return err @@ -731,7 +731,7 @@ func upgradeFromStorageTypeDir(name string, d *Daemon, defaultPoolName string, d if shared.PathExists(oldSnapshotMntPoint) && !shared.PathExists(newSnapshotMntPoint) { err := os.Rename(oldSnapshotMntPoint, newSnapshotMntPoint) if err != nil { - output, err := storageRsyncCopy(oldSnapshotMntPoint, newSnapshotMntPoint) + output, err := rsyncLocalCopy(oldSnapshotMntPoint, newSnapshotMntPoint, "") if err != nil { logger.Errorf("Failed to rsync: %s: %s.", output, err) return err @@ -1057,7 +1057,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d } // Use rsync to fill the empty volume. - output, err := storageRsyncCopy(oldContainerMntPoint, newContainerMntPoint) + output, err := rsyncLocalCopy(oldContainerMntPoint, newContainerMntPoint, "") if err != nil { ctStorage.ContainerDelete(ctStruct) return fmt.Errorf("rsync failed: %s", string(output)) @@ -1211,7 +1211,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d } // Use rsync to fill the empty volume. - output, err := storageRsyncCopy(oldSnapshotMntPoint, newSnapshotMntPoint) + output, err := rsyncLocalCopy(oldSnapshotMntPoint, newSnapshotMntPoint, "") if err != nil { csStorage.ContainerDelete(csStruct) return fmt.Errorf("rsync failed: %s", string(output)) diff --git a/lxd/rsync.go b/lxd/rsync.go index 456bd5a..44775f4 100644 --- a/lxd/rsync.go +++ b/lxd/rsync.go @@ -14,7 +14,36 @@ import ( "github.com/lxc/lxd/shared/logger" ) -func rsyncSendSetup(path string) (*exec.Cmd, net.Conn, io.ReadCloser, error) { +// rsyncCopy copies a directory using rsync (with the --devices option). +func rsyncLocalCopy(source string, dest string, bwlimit string) (string, error) { + err := os.MkdirAll(dest, 0755) + if err != nil { + return "", err + } + + rsyncVerbosity := "-q" + if debug { + rsyncVerbosity = "-vi" + } + + if bwlimit == "" { + bwlimit = "0" + } + + return shared.RunCommand("rsync", + "-a", + "-HAX", + "--devices", + "--delete", + "--checksum", + "--numeric-ids", + "--bwlimit", bwlimit, + rsyncVerbosity, + shared.AddSlash(source), + dest) +} + +func rsyncSendSetup(path string, bwlimit string) (*exec.Cmd, net.Conn, io.ReadCloser, error) { /* * It's sort of unfortunate, but there's no library call to get a * temporary name, so we get the file and close it and use its name. @@ -57,8 +86,11 @@ func rsyncSendSetup(path string) (*exec.Cmd, net.Conn, io.ReadCloser, error) { * hardcoding that at the other end, so we can just ignore it. */ rsyncCmd := fmt.Sprintf("sh -c \"%s netcat %s\"", execPath, f.Name()) - cmd := exec.Command( - "rsync", + if bwlimit == "" { + bwlimit = "0" + } + + cmd := exec.Command("rsync", "-arvP", "--devices", "--numeric-ids", @@ -66,7 +98,9 @@ func rsyncSendSetup(path string) (*exec.Cmd, net.Conn, io.ReadCloser, error) { path, "localhost:/tmp/foo", "-e", - rsyncCmd) + rsyncCmd, + "--bwlimit", + bwlimit) stderr, err := cmd.StderrPipe() if err != nil { @@ -90,8 +124,8 @@ func rsyncSendSetup(path string) (*exec.Cmd, net.Conn, io.ReadCloser, error) { // RsyncSend sets up the sending half of an rsync, to recursively send the // directory pointed to by path over the websocket. -func RsyncSend(path string, conn *websocket.Conn, readWrapper func(io.ReadCloser) io.ReadCloser) error { - cmd, dataSocket, stderr, err := rsyncSendSetup(path) +func RsyncSend(path string, conn *websocket.Conn, readWrapper func(io.ReadCloser) io.ReadCloser, bwlimit string) error { + cmd, dataSocket, stderr, err := rsyncSendSetup(path, bwlimit) if err != nil { return err } diff --git a/lxd/storage.go b/lxd/storage.go index 99e3ba7..98c8f69 100644 --- a/lxd/storage.go +++ b/lxd/storage.go @@ -112,33 +112,6 @@ func filesystemDetect(path string) (string, error) { } } -// storageRsyncCopy copies a directory using rsync (with the --devices option). -func storageRsyncCopy(source string, dest string) (string, error) { - err := os.MkdirAll(dest, 0755) - if err != nil { - return "", err - } - - rsyncVerbosity := "-q" - if debug { - rsyncVerbosity = "-vi" - } - - output, err := shared.RunCommand( - "rsync", - "-a", - "-HAX", - "--devices", - "--delete", - "--checksum", - "--numeric-ids", - rsyncVerbosity, - shared.AddSlash(source), - dest) - - return output, err -} - // storageType defines the type of a storage type storageType int diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go index cb14b58..5561663 100644 --- a/lxd/storage_btrfs.go +++ b/lxd/storage_btrfs.go @@ -463,7 +463,11 @@ func (s *storageBtrfs) StoragePoolUmount() (bool, error) { } func (s *storageBtrfs) StoragePoolUpdate(writable *api.StoragePoolPut, changedConfig []string) error { - return fmt.Errorf("Btrfs storage properties cannot be changed.") + if shared.StringInSlice("rsync.bwlimit", changedConfig) { + return nil + } + + return fmt.Errorf("Storage property cannot be changed.") } func (s *storageBtrfs) GetStoragePoolWritable() api.StoragePoolPut { @@ -1005,7 +1009,8 @@ func (s *storageBtrfs) ContainerRestore(container container, sourceContainer con if err == nil { // Use rsync to fill the empty volume. Sync by using // the subvolume name. - output, err := storageRsyncCopy(sourceContainerSubvolumeName, targetContainerSubvolumeName) + bwlimit := s.pool.Config["rsync.bwlimit"] + output, err := rsyncLocalCopy(sourceContainerSubvolumeName, targetContainerSubvolumeName, bwlimit) if err != nil { s.ContainerDelete(container) logger.Errorf("ContainerRestore: rsync failed: %s.", string(output)) @@ -1743,7 +1748,7 @@ func (s *btrfsMigrationSourceDriver) send(conn *websocket.Conn, btrfsPath string return err } -func (s *btrfsMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn, op *operation) error { +func (s *btrfsMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn, op *operation, bwlimit string) error { _, containerPool := s.container.Storage().GetContainerPoolInfo() containerName := s.container.Name() containersPath := getContainerMountPoint(containerPool, "") @@ -1821,7 +1826,7 @@ func (s *btrfsMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn, op * return s.send(conn, migrationSendSnapshot, btrfsParent, wrapper) } -func (s *btrfsMigrationSourceDriver) SendAfterCheckpoint(conn *websocket.Conn) error { +func (s *btrfsMigrationSourceDriver) SendAfterCheckpoint(conn *websocket.Conn, bwlimit string) error { tmpPath := containerPath(fmt.Sprintf("%s/.migration-send", s.container.Name()), true) err := os.MkdirAll(tmpPath, 0700) if err != nil { diff --git a/lxd/storage_dir.go b/lxd/storage_dir.go index c50a77b..27890df 100644 --- a/lxd/storage_dir.go +++ b/lxd/storage_dir.go @@ -159,7 +159,11 @@ func (s *storageDir) GetContainerPoolInfo() (int64, string) { } func (s *storageDir) StoragePoolUpdate(writable *api.StoragePoolPut, changedConfig []string) error { - return fmt.Errorf("Dir storage properties cannot be changed.") + if shared.StringInSlice("rsync.bwlimit", changedConfig) { + return nil + } + + return fmt.Errorf("Storage property cannot be changed.") } // Functions dealing with storage pools. @@ -367,7 +371,8 @@ func (s *storageDir) copyContainer(target container, source container) error { return err } - output, err := storageRsyncCopy(sourceContainerMntPoint, targetContainerMntPoint) + bwlimit := s.pool.Config["rsync.bwlimit"] + output, err := rsyncLocalCopy(sourceContainerMntPoint, targetContainerMntPoint, bwlimit) if err != nil { return fmt.Errorf("Failed to rsync container: %s: %s.", string(output), err) } @@ -400,7 +405,8 @@ func (s *storageDir) copySnapshot(target container, source container) error { return err } - output, err := storageRsyncCopy(sourceContainerMntPoint, targetContainerMntPoint) + bwlimit := s.pool.Config["rsync.bwlimit"] + output, err := rsyncLocalCopy(sourceContainerMntPoint, targetContainerMntPoint, bwlimit) if err != nil { return fmt.Errorf("Failed to rsync container: %s: %s.", string(output), err) } @@ -533,7 +539,8 @@ func (s *storageDir) ContainerRestore(container container, sourceContainer conta sourcePath := sourceContainer.Path() // Restore using rsync - output, err := storageRsyncCopy(sourcePath, targetPath) + bwlimit := s.pool.Config["rsync.bwlimit"] + output, err := rsyncLocalCopy(sourcePath, targetPath, bwlimit) if err != nil { return fmt.Errorf("Failed to rsync container: %s: %s.", string(output), err) } @@ -566,8 +573,8 @@ func (s *storageDir) ContainerSnapshotCreate(snapshotContainer container, source return err } - rsync := func(snapshotContainer container, oldPath string, newPath string) error { - output, err := storageRsyncCopy(oldPath, newPath) + rsync := func(snapshotContainer container, oldPath string, newPath string, bwlimit string) error { + output, err := rsyncLocalCopy(oldPath, newPath, bwlimit) if err != nil { s.ContainerDelete(snapshotContainer) return fmt.Errorf("Failed to rsync: %s: %s.", string(output), err) @@ -586,7 +593,8 @@ func (s *storageDir) ContainerSnapshotCreate(snapshotContainer container, source _, sourcePool := sourceContainer.Storage().GetContainerPoolInfo() sourceContainerName := sourceContainer.Name() sourceContainerMntPoint := getContainerMountPoint(sourcePool, sourceContainerName) - err = rsync(snapshotContainer, sourceContainerMntPoint, targetContainerMntPoint) + bwlimit := s.pool.Config["rsync.bwlimit"] + err = rsync(snapshotContainer, sourceContainerMntPoint, targetContainerMntPoint, bwlimit) if err != nil { return err } @@ -602,7 +610,7 @@ func (s *storageDir) ContainerSnapshotCreate(snapshotContainer container, source return nil } - err = rsync(snapshotContainer, sourceContainerMntPoint, targetContainerMntPoint) + err = rsync(snapshotContainer, sourceContainerMntPoint, targetContainerMntPoint, bwlimit) if err != nil { return err } diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go index f521ad2..22e65e3 100644 --- a/lxd/storage_lvm.go +++ b/lxd/storage_lvm.go @@ -915,19 +915,20 @@ func (s *storageLvm) StoragePoolUpdate(writable *api.StoragePoolPut, changedConf return fmt.Errorf("The \"zfs.pool_name\" property does not apply to LVM drivers.") } + if shared.StringInSlice("lvm.use_thinpool", changedConfig) { + return fmt.Errorf("The \"lvm.use_thinpool\" property cannot be changed.") + } + // "volume.block.mount_options" requires no on-disk modifications. // "volume.block.filesystem" requires no on-disk modifications. // "volume.size" requires no on-disk modifications. + // "rsync.bwlimit" requires no on-disk modifications. // Given a set of changeable pool properties the change should be // "transactional": either the whole update succeeds or none. So try to // revert on error. revert := true - if shared.StringInSlice("lvm.use_thinpool", changedConfig) { - return fmt.Errorf("The \"lvm.use_thinpool\" property cannot be changed.") - } - if shared.StringInSlice("lvm.thinpool_name", changedConfig) { if !s.useThinpool { return fmt.Errorf("The LVM storage pool \"%s\" does not use thin pools. The \"lvm.thinpool_name\" porperty cannot be set.", s.pool.Name) @@ -1370,7 +1371,9 @@ func (s *storageLvm) copyContainerLv(target container, source container, readonl } defer source.Unfreeze() } - output, err := storageRsyncCopy(sourceContainerMntPoint, targetContainerMntPoint) + + bwlimit := s.pool.Config["rsync.bwlimit"] + output, err := rsyncLocalCopy(sourceContainerMntPoint, targetContainerMntPoint, bwlimit) if err != nil { return fmt.Errorf("Failed to rsync container: %s: %s.", string(output), err) } @@ -1722,7 +1725,9 @@ func (s *storageLvm) ContainerRestore(target container, source container) error if err != nil { } defer target.Unfreeze() - output, err := storageRsyncCopy(sourceContainerMntPoint, targetContainerMntPoint) + + bwlimit := s.pool.Config["rsync.bwlimit"] + output, err := rsyncLocalCopy(sourceContainerMntPoint, targetContainerMntPoint, bwlimit) if err != nil { return fmt.Errorf("Failed to rsync container: %s: %s.", string(output), err) } diff --git a/lxd/storage_migration.go b/lxd/storage_migration.go index 67755b0..b207374 100644 --- a/lxd/storage_migration.go +++ b/lxd/storage_migration.go @@ -16,14 +16,14 @@ type MigrationStorageSourceDriver interface { /* send any bits of the container/snapshots that are possible while the * container is still running. */ - SendWhileRunning(conn *websocket.Conn, op *operation) error + SendWhileRunning(conn *websocket.Conn, op *operation, bwlimit string) error /* send the final bits (e.g. a final delta snapshot for zfs, btrfs, or * do a final rsync) of the fs after the container has been * checkpointed. This will only be called when a container is actually * being live migrated. */ - SendAfterCheckpoint(conn *websocket.Conn) error + SendAfterCheckpoint(conn *websocket.Conn, bwlimit string) error /* Called after either success or failure of a migration, can be used * to clean up any temporary snapshots, etc. @@ -40,7 +40,7 @@ func (s rsyncStorageSourceDriver) Snapshots() []container { return s.snapshots } -func (s rsyncStorageSourceDriver) SendWhileRunning(conn *websocket.Conn, op *operation) error { +func (s rsyncStorageSourceDriver) SendWhileRunning(conn *websocket.Conn, op *operation, bwlimit string) error { for _, send := range s.snapshots { ourStart, err := send.StorageStart() if err != nil { @@ -52,18 +52,19 @@ func (s rsyncStorageSourceDriver) SendWhileRunning(conn *websocket.Conn, op *ope path := send.Path() wrapper := StorageProgressReader(op, "fs_progress", send.Name()) - if err := RsyncSend(shared.AddSlash(path), conn, wrapper); err != nil { + err = RsyncSend(shared.AddSlash(path), conn, wrapper, bwlimit) + if err != nil { return err } } wrapper := StorageProgressReader(op, "fs_progress", s.container.Name()) - return RsyncSend(shared.AddSlash(s.container.Path()), conn, wrapper) + return RsyncSend(shared.AddSlash(s.container.Path()), conn, wrapper, bwlimit) } -func (s rsyncStorageSourceDriver) SendAfterCheckpoint(conn *websocket.Conn) error { +func (s rsyncStorageSourceDriver) SendAfterCheckpoint(conn *websocket.Conn, bwlimit string) error { // resync anything that changed between our first send and the checkpoint - return RsyncSend(shared.AddSlash(s.container.Path()), conn, nil) + return RsyncSend(shared.AddSlash(s.container.Path()), conn, nil, bwlimit) } func (s rsyncStorageSourceDriver) Cleanup() { diff --git a/lxd/storage_pools_config.go b/lxd/storage_pools_config.go index ace7122..2c97c07 100644 --- a/lxd/storage_pools_config.go +++ b/lxd/storage_pools_config.go @@ -51,6 +51,7 @@ var storagePoolConfigKeys = map[string]func(value string) error{ // valid drivers: zfs "zfs.clone_copy": shared.IsBool, "zfs.pool_name": shared.IsAny, + "rsync.bwlimit": shared.IsAny, } func storagePoolValidateConfig(name string, driver string, config map[string]string) error { @@ -68,6 +69,14 @@ func storagePoolValidateConfig(name string, driver string, config map[string]str } } + v, ok := config["rsync.bwlimit"] + if ok && v != "" { + _, err := shared.ParseByteSizeString(v) + if err != nil { + return err + } + } + // Check whether the config properties for the driver container sane // values. for key, val := range config { diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go index e56491f..bd02bb6 100644 --- a/lxd/storage_zfs.go +++ b/lxd/storage_zfs.go @@ -380,6 +380,8 @@ func (s *storageZfs) StoragePoolUpdate(writable *api.StoragePoolPut, changedConf return fmt.Errorf("The \"zfs.pool_name\" property cannot be changed.") } + // "rsync.bwlimit" requires no on-disk modifications. + logger.Infof("Updated ZFS storage pool \"%s\".", s.pool.Name) return nil } @@ -808,7 +810,8 @@ func (s *storageZfs) copyWithoutSnapshotsSparse(target container, source contain s.ContainerDelete(target) }() - output, err := storageRsyncCopy(sourceContainerPath, targetContainerPath) + bwlimit := s.pool.Config["rsync.bwlimit"] + output, err := rsyncLocalCopy(sourceContainerPath, targetContainerPath, bwlimit) if err != nil { return fmt.Errorf("rsync failed: %s", string(output)) } @@ -2421,7 +2424,7 @@ func (s *zfsMigrationSourceDriver) send(conn *websocket.Conn, zfsName string, zf return err } -func (s *zfsMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn, op *operation) error { +func (s *zfsMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn, op *operation, bwlimit string) error { if s.container.IsSnapshot() { fields := strings.SplitN(s.container.Name(), shared.SnapshotDelimiter, 2) snapshotName := fmt.Sprintf("snapshot-%s", fields[1]) @@ -2458,7 +2461,7 @@ func (s *zfsMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn, op *op return nil } -func (s *zfsMigrationSourceDriver) SendAfterCheckpoint(conn *websocket.Conn) error { +func (s *zfsMigrationSourceDriver) SendAfterCheckpoint(conn *websocket.Conn, bwlimit string) error { s.stoppedSnapName = fmt.Sprintf("migration-send-%s", uuid.NewRandom().String()) if err := s.zfs.zfsPoolVolumeSnapshotCreate(fmt.Sprintf("containers/%s", s.container.Name()), s.stoppedSnapName); err != nil { return err From 4c27f85740541c14ec3dbbfb6485f1995e110c53 Mon Sep 17 00:00:00 2001 From: Christian Brauner <[email protected]> Date: Tue, 18 Apr 2017 15:21:23 +0200 Subject: [PATCH 2/2] test: test whether rsync.bwlimit can be set Signed-off-by: Christian Brauner <[email protected]> --- test/suites/storage.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/suites/storage.sh b/test/suites/storage.sh index 998d83f..3ddcc72 100644 --- a/test/suites/storage.sh +++ b/test/suites/storage.sh @@ -58,6 +58,9 @@ test_storage() { lxc storage create "lxdtest-$(basename "${LXD_DIR}")-valid-zfs-pool-config" zfs zfs.pool_name="lxdtest-$(basename "${LXD_DIR}")-valid-zfs-pool-config" lxc storage delete "lxdtest-$(basename "${LXD_DIR}")-valid-zfs-pool-config" + + lxc storage create "lxdtest-$(basename "${LXD_DIR}")-valid-zfs-pool-config" zfs rsync.bwlimit=1024 + lxc storage delete "lxdtest-$(basename "${LXD_DIR}")-valid-zfs-pool-config" fi if which btrfs >/dev/null 2>&1; then @@ -83,6 +86,9 @@ test_storage() { ! lxc storage create "lxdtest-$(basename "${LXD_DIR}")-invalid-btrfs-pool-config" btrfs volume.zfs.use_refquota=true ! lxc storage create "lxdtest-$(basename "${LXD_DIR}")-invalid-btrfs-pool-config" btrfs zfs.clone_copy=true ! lxc storage create "lxdtest-$(basename "${LXD_DIR}")-invalid-btrfs-pool-config" btrfs zfs.pool_name=bla + + lxc storage create "lxdtest-$(basename "${LXD_DIR}")-valid-btrfs-pool-config" btrfs rsync.bwlimit=1024 + lxc storage delete "lxdtest-$(basename "${LXD_DIR}")-valid-btrfs-pool-config" fi # Create dir pool. @@ -109,6 +115,9 @@ test_storage() { ! lxc storage create "lxdtest-$(basename "${LXD_DIR}")-invalid-dir-pool-config" dir zfs.clone_copy=true ! lxc storage create "lxdtest-$(basename "${LXD_DIR}")-invalid-dir-pool-config" dir zfs.pool_name=bla + lxc storage create "lxdtest-$(basename "${LXD_DIR}")-valid-dir-pool-config" dir rsync.bwlimit=1024 + lxc storage delete "lxdtest-$(basename "${LXD_DIR}")-valid-dir-pool-config" + if which lvdisplay >/dev/null 2>&1; then # Create lvm pool. configure_loop_device loop_file_3 loop_device_3 @@ -162,6 +171,7 @@ test_storage() { lxc storage create "lxdtest-$(basename "${LXD_DIR}")-valid-lvm-pool-config-pool21" lvm volume.size=2GB lxc storage create "lxdtest-$(basename "${LXD_DIR}")-valid-lvm-pool-config-pool22" lvm lvm.use_thinpool=true lxc storage create "lxdtest-$(basename "${LXD_DIR}")-valid-lvm-pool-config-pool23" lvm lvm.use_thinpool=true lvm.thinpool_name="lxdtest-$(basename "${LXD_DIR}")-valid-lvm-pool-config" + lxc storage create "lxdtest-$(basename "${LXD_DIR}")-valid-lvm-pool-config-pool24" lvm rsync.bwlimit=1024 fi # Set default storage pool for image import. @@ -641,6 +651,9 @@ test_storage() { lxc storage delete "lxdtest-$(basename "${LXD_DIR}")-valid-lvm-pool-config-pool23" vgremove -ff "lxdtest-$(basename "${LXD_DIR}")-non-thinpool-pool23" || true + + lxc storage delete "lxdtest-$(basename "${LXD_DIR}")-valid-lvm-pool-config-pool24" + vgremove -ff "lxdtest-$(basename "${LXD_DIR}")-non-thinpool-pool24" || true fi )
_______________________________________________ lxc-devel mailing list [email protected] http://lists.linuxcontainers.org/listinfo/lxc-devel
