From: Milind Changire Date: Mon, 4 Nov 2019 10:47:32 +0000 (+0530) Subject: cephfs-shell: add snapshot management X-Git-Tag: v15.1.0~746^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=73597e94f1d0e2b381592a0f3d1c569b69745c1f;p=ceph.git cephfs-shell: add snapshot management Snapshots can be managed by: $ snap {create|delete} snap_name dir_name Fixes: http://tracker.ceph.com/issues/38681 Signed-off-by: Milind Changire --- diff --git a/doc/cephfs/cephfs-shell.rst b/doc/cephfs/cephfs-shell.rst index 2d4c36d5fd19..496a541a8666 100644 --- a/doc/cephfs/cephfs-shell.rst +++ b/doc/cephfs/cephfs-shell.rst @@ -380,3 +380,17 @@ Usage : Options : -h Shows the help message + +snap +---- + +Create or Delete Snapshot + +Usage: + + snap {create|delete} + +* snap_name - Snapshot name to be created or deleted + +* dir_name - directory under which snapshot should be created or deleted + diff --git a/qa/tasks/cephfs/test_cephfs_shell.py b/qa/tasks/cephfs/test_cephfs_shell.py index da8ceb6fed79..742433ba4bda 100644 --- a/qa/tasks/cephfs/test_cephfs_shell.py +++ b/qa/tasks/cephfs/test_cephfs_shell.py @@ -48,6 +48,11 @@ class TestCephFSShell(CephFSTestCase): return mount_x.client_remote.run(args=args, stdout=StringIO(), stdin=stdin) + def get_cephfs_shell_cmd_error(self, cmd, mount_x=None, opts=None, + stdin=None): + return self.run_cephfs_shell_cmd(cmd, mount_x, opts, stdin).stderr.\ + getvalue().strip() + def get_cephfs_shell_cmd_output(self, cmd, mount_x=None, opts=None, stdin=None): return self.run_cephfs_shell_cmd(cmd, mount_x, opts, stdin).stdout.\ @@ -293,6 +298,71 @@ class TestGetAndPut(TestCephFSShell): log.info("o_hash:{}".format(o_hash)) assert(s_hash == o_hash) +class TestSnapshots(TestCephFSShell): + def test_snap(self): + """ + Test that snapshot creation and deletion work + """ + sd = self.fs.get_config('client_snapdir') + sdn = "data_dir/{}/snap1".format(sd) + + # create a data dir and dump some files into it + self.get_cephfs_shell_cmd_output("mkdir data_dir") + s = 'A' * 10240 + o = self.get_cephfs_shell_cmd_output("put - data_dir/data_a", stdin=s) + s = 'B' * 10240 + o = self.get_cephfs_shell_cmd_output("put - data_dir/data_b", stdin=s) + s = 'C' * 10240 + o = self.get_cephfs_shell_cmd_output("put - data_dir/data_c", stdin=s) + s = 'D' * 10240 + o = self.get_cephfs_shell_cmd_output("put - data_dir/data_d", stdin=s) + s = 'E' * 10240 + o = self.get_cephfs_shell_cmd_output("put - data_dir/data_e", stdin=s) + + o = self.get_cephfs_shell_cmd_output("ls -l /data_dir") + log.info("cephfs-shell output:\n{}".format(o)) + + # create the snapshot - must pass + o = self.get_cephfs_shell_cmd_output("snap create snap1 /data_dir") + log.info("cephfs-shell output:\n{}".format(o)) + assert(o == "") + o = self.mount_a.stat(sdn) + log.info("mount_a output:\n{}".format(o)) + assert(('st_mode' in str(o)) == True) + + # create the same snapshot again - must fail with an error message + o = self.get_cephfs_shell_cmd_error("snap create snap1 /data_dir") + log.info("cephfs-shell output:\n{}".format(o)) + o = o.split('\n') + assert(o[0] == "ERROR: snapshot 'snap1' already exists") + o = self.mount_a.stat(sdn) + log.info("mount_a output:\n{}".format(o)) + assert(('st_mode' in str(o)) == True) + + # delete the snapshot - must pass + o = self.get_cephfs_shell_cmd_output("snap delete snap1 /data_dir") + log.info("cephfs-shell output:\n{}".format(o)) + assert(o == "") + try: + o = self.mount_a.stat(sdn) + except: + # snap dir should not exist anymore + pass + log.info("mount_a output:\n{}".format(o)) + assert(('st_mode' in str(o)) == False) + + # delete the same snapshot again - must fail with an error message + o = self.get_cephfs_shell_cmd_error("snap delete snap1 /data_dir") + o = o.strip() + o = o.split('\n') + assert(o[0] == "ERROR: 'snap1': no such snapshot") + try: + o = self.mount_a.stat(sdn) + except: + pass + log.info("mount_a output:\n{}".format(o)) + assert(('st_mode' in str(o)) == False) + class TestCD(TestCephFSShell): CLIENTS_REQUIRED = 1 diff --git a/src/tools/cephfs/cephfs-shell b/src/tools/cephfs/cephfs-shell index d672f02c5385..d74a10a0b9a9 100755 --- a/src/tools/cephfs/cephfs-shell +++ b/src/tools/cephfs/cephfs-shell @@ -172,8 +172,9 @@ def get_all_possible_paths(pattern): for pattern in patterns: for path in paths: paths.extend(glob(path, pattern)) - return [path for path in paths if fnmatch.fnmatch(path, - os.path.join(cephfs.getcwd(), complete_pattern))] + if is_rel_path: + complete_pattern = os.path.join(cephfs.getcwd(), complete_pattern) + return [path for path in paths if fnmatch.fnmatch(path, complete_pattern)] suffixes = ['B', 'K', 'M', 'G', 'T', 'P'] @@ -822,6 +823,9 @@ sub-directories, files') @with_argparser(rmdir_parser) def do_rmdir(self, args): + self.do_rmdir_helper(args) + + def do_rmdir_helper(self, args): """ Remove a specific Directory """ @@ -1245,6 +1249,50 @@ sub-directories, files') perror('max_files is not set') pass + snap_parser = argparse.ArgumentParser(description='Snapshot Management') + snap_parser.add_argument('op', type=str, + help='Snapshot operation: create or delete') + snap_parser.add_argument('name', type=str, action=path_to_bytes, + help='Name of snapshot') + snap_parser.add_argument('dir', type=str, action=path_to_bytes, + help='Directory for which snapshot ' + 'needs to be created or deleted') + + @with_argparser(snap_parser) + def do_snap(self, args): + """ + Snapshot management for the volume + """ + # setting self.colors to None turns off colorizing and + # perror emits plain text + self.colors = None + + snapdir = '.snap' + conf_snapdir = cephfs.conf_get('client_snapdir') + if conf_snapdir is not None: + snapdir = conf_snapdir + snapdir = to_bytes(snapdir) + if args.op == 'create': + try: + if is_dir_exists(args.dir): + cephfs.mkdir(os.path.join(args.dir, snapdir, args.name), 0o755) + else: + self.perror("'{}': no such directory".format(args.dir.decode('utf-8'))) + except libcephfs.Error: + self.perror("snapshot '{}' already exists".format(args.name.decode('utf-8'))) + elif args.op == 'delete': + snap_dir = os.path.join(args.dir, snapdir, args.name) + try: + if is_dir_exists(snap_dir): + newargs = argparse.Namespace(paths=[snap_dir], parent=False) + self.do_rmdir_helper(newargs) + else: + self.perror("'{}': no such snapshot".format(args.name.decode('utf-8'))) + except libcephfs.Error: + self.perror("error while deleting '{}'".format(snap_dir.decode('utf-8'))) + else: + self.perror("snapshot can only be created or deleted; check - help snap") + def do_help(self, line): """ Get details about a command.