From 858f3d18118d59490cf3c01c0ceba44bfb05b646 Mon Sep 17 00:00:00 2001 From: Iustin Pop <iustin@google.com> Date: Fri, 21 Aug 2009 15:58:44 +0200 Subject: [PATCH] Add disk copy support at backend and the rpc level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This uses a simple 'dd if=β¦ | ssh $target dd of=β¦' method, like the ExportSnapshot (which uses the OS export; here we want full disk-level copy and not any FS-level changes). Signed-off-by: Iustin Pop <iustin@google.com> Reviewed-by: Michael Hanselmann <hansmi@google.com> --- daemons/ganeti-noded | 9 +++++++++ lib/backend.py | 48 ++++++++++++++++++++++++++++++++++++++++++++ lib/rpc.py | 11 ++++++++++ 3 files changed, 68 insertions(+) diff --git a/daemons/ganeti-noded b/daemons/ganeti-noded index 58d755e10..3e3d6a0c3 100755 --- a/daemons/ganeti-noded +++ b/daemons/ganeti-noded @@ -256,6 +256,15 @@ class NodeHttpServer(http.server.HttpServer): disks = [objects.Disk.FromDict(cf) for cf in params[0]] return backend.BlockdevGetsize(disks) + @staticmethod + def perspective_blockdev_export(params): + """Compute the sizes of the given block devices. + + """ + disk = objects.Disk.FromDict(params[0]) + dest_node, dest_path, cluster_name = params[1:] + return backend.BlockdevExport(disk, dest_node, dest_path, cluster_name) + # blockdev/drbd specific methods ---------- @staticmethod diff --git a/lib/backend.py b/lib/backend.py index 30d61ce58..bf2510eef 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -1459,6 +1459,54 @@ def BlockdevGetsize(disks): return result +def BlockdevExport(disk, dest_node, dest_path, cluster_name): + """Export a block device to a remote node. + + @type disk: L{objects.Disk} + @param disk: the description of the disk to export + @type dest_node: str + @param dest_node: the destination node to export to + @type dest_path: str + @param dest_path: the destination path on the target node + @type cluster_name: str + @param cluster_name: the cluster name, needed for SSH hostalias + @rtype: None + + """ + real_disk = _RecursiveFindBD(disk) + if real_disk is None: + _Fail("Block device '%s' is not set up", disk) + + real_disk.Open() + + # the block size on the read dd is 1MiB to match our units + expcmd = utils.BuildShellCmd("set -e; set -o pipefail; " + "dd if=%s bs=1048576 count=%s", + real_disk.dev_path, str(disk.size)) + + # we set here a smaller block size as, due to ssh buffering, more + # than 64-128k will mostly ignored; we use nocreat to fail if the + # device is not already there or we pass a wrong path; we use + # notrunc to no attempt truncate on an LV device; we use oflag=dsync + # to not buffer too much memory; this means that at best, we flush + # every 64k, which will not be very fast + destcmd = utils.BuildShellCmd("dd of=%s conv=nocreat,notrunc bs=65536" + " oflag=dsync", dest_path) + + remotecmd = _GetSshRunner(cluster_name).BuildCmd(dest_node, + constants.GANETI_RUNAS, + destcmd) + + # all commands have been checked, so we're safe to combine them + command = '|'.join([expcmd, utils.ShellQuoteArgs(remotecmd)]) + + result = utils.RunCmd(["bash", "-c", command]) + + if result.failed: + _Fail("Disk copy command '%s' returned error: %s" + " output: %s", command, result.fail_reason, result.output) + + def UploadFile(file_name, data, mode, uid, gid, atime, mtime): """Write a file to the filesystem. diff --git a/lib/rpc.py b/lib/rpc.py index 235754e21..98f0f6ed6 100644 --- a/lib/rpc.py +++ b/lib/rpc.py @@ -961,6 +961,17 @@ class RpcRunner(object): return self._SingleNodeCall(node, "blockdev_grow", [cf_bdev.ToDict(), amount]) + def call_blockdev_export(self, node, cf_bdev, + dest_node, dest_path, cluster_name): + """Export a given disk to another node. + + This is a single-node call. + + """ + return self._SingleNodeCall(node, "blockdev_export", + [cf_bdev.ToDict(), dest_node, dest_path, + cluster_name]) + def call_blockdev_snapshot(self, node, cf_bdev): """Request a snapshot of the given block device. -- GitLab