Whamcloud - gitweb
LU-5732 hsm: complementary testsuite for user HSM API 36/12836/7
authorFrank Zago <fzago@cray.com>
Thu, 9 Oct 2014 00:56:08 +0000 (19:56 -0500)
committerOleg Drokin <oleg.drokin@intel.com>
Fri, 16 Jan 2015 03:26:46 +0000 (03:26 +0000)
A few tests for the userspace Lustre HSM API. Tests
registration, archiving, progress report amd HSM state
of files.

Change-Id: I64ba3361d6a7b768b133b584e551724cf2c5bee2
Signed-off-by: frank zago <fzago@cray.com>
Reviewed-on: http://review.whamcloud.com/12836
Tested-by: Jenkins
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: Jian Yu <jian.yu@intel.com>
Reviewed-by: Henri Doreau <henri.doreau@cea.fr>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
lustre/tests/Makefile.am
lustre/tests/llapi_hsm_test.c [new file with mode: 0644]
lustre/tests/sanity-hsm.sh

index 1116aa7..25071e7 100644 (file)
@@ -74,7 +74,7 @@ noinst_PROGRAMS += openfilleddirunlink rename_many memhog
 noinst_PROGRAMS += mmap_sanity writemany reads flocks_test flock_deadlock
 noinst_PROGRAMS += write_time_limit rwv lgetxattr_size_check checkfiemap
 noinst_PROGRAMS += listxattr_size_check check_fhandle_syscalls badarea_io
-noinst_PROGRAMS += llapi_layout_test orphan_linkea_check
+noinst_PROGRAMS += llapi_layout_test orphan_linkea_check llapi_hsm_test
 
 bin_PROGRAMS = mcreate munlink
 testdir = $(libdir)/lustre/tests
@@ -89,6 +89,7 @@ mmap_sanity_SOURCES= mmap_sanity.c
 LIBLUSTREAPI = $(top_builddir)/lustre/utils/liblustreapi.a
 multiop_LDADD=$(LIBLUSTREAPI) $(PTHREAD_LIBS) $(LIBCFS)
 llapi_layout_test_LDADD=$(LIBLUSTREAPI)
+llapi_hsm_test_LDADD=$(LIBLUSTREAPI)
 it_test_LDADD=$(LIBCFS)
 rwv_LDADD=$(LIBCFS)
 
diff --git a/lustre/tests/llapi_hsm_test.c b/lustre/tests/llapi_hsm_test.c
new file mode 100644 (file)
index 0000000..e21b4b7
--- /dev/null
@@ -0,0 +1,1041 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+
+/* Copyright 2014 Cray Inc, all rights reserved. */
+/* Some portions are extracted from llapi_layout_test.c */
+
+/* The purpose of this test is to check some HSM functions. HSM must
+ * be enabled before running it:
+ *   echo enabled > /proc/fs/lustre/mdt/lustre-MDT0000/hsm_control
+ */
+
+/* All tests return 0 on success and non zero on error. The program will
+ * exit as soon a non zero error is returned. */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <poll.h>
+#include <time.h>
+
+#include <lustre/lustreapi.h>
+
+static char fsmountdir[PATH_MAX];      /* Lustre mountpoint */
+static char *lustre_dir;               /* Test directory inside Lustre */
+
+#define ERROR(fmt, ...)                                                        \
+       fprintf(stderr, "%s: %s:%d: %s: " fmt "\n",                     \
+               program_invocation_short_name, __FILE__, __LINE__,      \
+               __func__, ## __VA_ARGS__);
+
+#define DIE(fmt, ...)                          \
+       do {                                    \
+               ERROR(fmt, ## __VA_ARGS__);     \
+               exit(EXIT_FAILURE);             \
+       } while (0)
+
+#define ASSERTF(cond, fmt, ...)                                                \
+       do {                                                            \
+               if (!(cond))                                            \
+                       DIE("assertion '%s' failed: "fmt,               \
+                           #cond, ## __VA_ARGS__);                     \
+       } while (0)                                                     \
+
+#define PERFORM(testfn) \
+       do {                                                            \
+               fprintf(stderr, "Starting test " #testfn " at %llu\n",  \
+                       (unsigned long long)time(NULL));                \
+               testfn();                                               \
+               fprintf(stderr, "Finishing test " #testfn " at %llu\n", \
+                      (unsigned long long)time(NULL));                 \
+       } while (0)
+
+/* Register and unregister 2000 times. Ensures there is no fd leak
+ * since there is usually 1024 fd per process. */
+int test1(void)
+{
+       int i;
+       int rc;
+       struct hsm_copytool_private *ctdata;
+
+       for (i = 0; i < 2000; i++) {
+               rc = llapi_hsm_copytool_register(&ctdata, fsmountdir,
+                                                0, NULL, 0);
+               ASSERTF(rc == 0,
+                       "llapi_hsm_copytool_register failed: %s, loop=%d",
+                       strerror(-rc), i);
+
+               rc = llapi_hsm_copytool_unregister(&ctdata);
+               ASSERTF(rc == 0,
+                       "llapi_hsm_copytool_unregister failed: %s, loop=%d",
+                       strerror(-rc), i);
+       }
+
+       return 0;
+}
+
+/* Re-register */
+int test2(void)
+{
+       int rc;
+       struct hsm_copytool_private *ctdata1;
+       struct hsm_copytool_private *ctdata2;
+
+       rc = llapi_hsm_copytool_register(&ctdata1, fsmountdir, 0, NULL, 0);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_register failed: %s",
+               strerror(-rc));
+
+       rc = llapi_hsm_copytool_register(&ctdata2, fsmountdir, 0, NULL, 0);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_register failed: %s",
+               strerror(-rc));
+
+       rc = llapi_hsm_copytool_unregister(&ctdata2);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_unregister failed: %s",
+               strerror(-rc));
+
+       rc = llapi_hsm_copytool_unregister(&ctdata1);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_unregister failed: %s",
+               strerror(-rc));
+
+       return 0;
+}
+
+/* Bad parameters to llapi_hsm_copytool_register(). */
+int test3(void)
+{
+       int rc;
+       struct hsm_copytool_private *ctdata;
+       int archives[33];
+
+       rc = llapi_hsm_copytool_register(&ctdata, fsmountdir, 1, NULL, 0);
+       ASSERTF(rc == -EINVAL, "llapi_hsm_copytool_register error: %s",
+               strerror(-rc));
+
+       rc = llapi_hsm_copytool_register(&ctdata, fsmountdir, 33, NULL, 0);
+       ASSERTF(rc == -EINVAL, "llapi_hsm_copytool_register error: %s",
+               strerror(-rc));
+
+       memset(archives, 1, sizeof(archives));
+       rc = llapi_hsm_copytool_register(&ctdata, fsmountdir, 34, archives, 0);
+       ASSERTF(rc == -EINVAL, "llapi_hsm_copytool_register error: %s",
+               strerror(-rc));
+
+#if 0
+       /* BUG? Should that fail or not? */
+       rc = llapi_hsm_copytool_register(&ctdata, fsmountdir, -1, NULL, 0);
+       ASSERTF(rc == -EINVAL, "llapi_hsm_copytool_register error: %s",
+               strerror(-rc));
+#endif
+
+       memset(archives, -1, sizeof(archives));
+       rc = llapi_hsm_copytool_register(&ctdata, fsmountdir, 1, archives, 0);
+       ASSERTF(rc == -EINVAL, "llapi_hsm_copytool_register error: %s",
+               strerror(-rc));
+
+       rc = llapi_hsm_copytool_register(&ctdata, "/tmp", 0, NULL, 0);
+       ASSERTF(rc == -ENOENT, "llapi_hsm_copytool_register error: %s",
+               strerror(-rc));
+
+       return 0;
+}
+
+/* Bad parameters to llapi_hsm_copytool_unregister(). */
+int test4(void)
+{
+       int rc;
+
+       rc = llapi_hsm_copytool_unregister(NULL);
+       ASSERTF(rc == -EINVAL, "llapi_hsm_copytool_unregister error: %s",
+               strerror(-rc));
+
+       return 0;
+}
+
+/* Test llapi_hsm_copytool_recv in non blocking mode */
+int test5(void)
+{
+       int rc;
+       int i;
+       struct hsm_copytool_private *ctdata;
+       struct hsm_action_list  *hal;
+       int msgsize;
+
+       rc = llapi_hsm_copytool_register(&ctdata, fsmountdir,
+                                        0, NULL, O_NONBLOCK);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_unregister failed: %s",
+               strerror(-rc));
+
+       /* Hopefully there is nothing lingering */
+       for (i = 0; i < 1000; i++) {
+               rc = llapi_hsm_copytool_recv(ctdata, &hal, &msgsize);
+               ASSERTF(rc == -EWOULDBLOCK, "llapi_hsm_copytool_recv error: %s",
+                       strerror(-rc));
+       }
+
+       rc = llapi_hsm_copytool_unregister(&ctdata);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_unregister failed: %s",
+               strerror(-rc));
+
+       return 0;
+}
+
+/* Test llapi_hsm_copytool_recv with bogus parameters */
+int test6(void)
+{
+       struct hsm_copytool_private *ctdata;
+       struct hsm_action_list *hal;
+       int rc;
+       int msgsize;
+
+       rc = llapi_hsm_copytool_register(&ctdata, fsmountdir, 0, NULL, 0);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_unregister failed: %s",
+               strerror(-rc));
+
+       rc = llapi_hsm_copytool_recv(NULL, &hal, &msgsize);
+       ASSERTF(rc == -EINVAL, "llapi_hsm_copytool_recv error: %s",
+               strerror(-rc));
+
+       rc = llapi_hsm_copytool_recv(ctdata, NULL, &msgsize);
+       ASSERTF(rc == -EINVAL, "llapi_hsm_copytool_recv error: %s",
+               strerror(-rc));
+
+       rc = llapi_hsm_copytool_recv(ctdata, &hal, NULL);
+       ASSERTF(rc == -EINVAL, "llapi_hsm_copytool_recv error: %s",
+               strerror(-rc));
+
+       rc = llapi_hsm_copytool_recv(ctdata, NULL, NULL);
+       ASSERTF(rc == -EINVAL, "llapi_hsm_copytool_recv error: %s",
+               strerror(-rc));
+
+       rc = llapi_hsm_copytool_unregister(&ctdata);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_unregister failed: %s",
+               strerror(-rc));
+
+       return 0;
+}
+
+/* Test polling (without actual traffic) */
+int test7(void)
+{
+       int rc;
+       struct hsm_copytool_private *ctdata;
+       struct hsm_action_list  *hal;
+       int msgsize;
+       int fd;
+       struct pollfd fds[1];
+
+       rc = llapi_hsm_copytool_register(&ctdata, fsmountdir,
+                                        0, NULL, O_NONBLOCK);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_register failed: %s",
+               strerror(-rc));
+
+       fd = llapi_hsm_copytool_get_fd(ctdata);
+       ASSERTF(fd >= 0, "llapi_hsm_copytool_get_fd failed: %s",
+               strerror(-rc));
+
+       /* Ensure it's read-only */
+       rc = write(fd, &rc, 1);
+       ASSERTF(rc == -1 && errno == EBADF, "write error: %d, %s",
+               rc, strerror(errno));
+
+       rc = llapi_hsm_copytool_recv(ctdata, &hal, &msgsize);
+       ASSERTF(rc == -EWOULDBLOCK, "llapi_hsm_copytool_recv error: %s",
+               strerror(-rc));
+
+       fds[0].fd = fd;
+       fds[0].events = POLLIN;
+       rc = poll(fds, 1, 10);
+       ASSERTF(rc == 0, "poll failed: %d, %s",
+               rc, strerror(errno)); /* no event */
+
+       rc = llapi_hsm_copytool_unregister(&ctdata);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_unregister failed: %s",
+               strerror(-rc));
+
+       return 0;
+}
+
+/* Create the testfile of a given length. It returns a valid file
+ * descriptor. */
+static char testfile[PATH_MAX];
+static int create_testfile(size_t length)
+{
+       int rc;
+       int fd;
+
+       rc = snprintf(testfile, sizeof(testfile), "%s/hsm_check_test",
+                     lustre_dir);
+       ASSERTF((rc > 0 && rc < sizeof(testfile)), "invalid name for testfile");
+
+       /* Remove old test file, if any. */
+       unlink(testfile);
+
+       /* Use truncate so we can create a file (almost) as big as we
+        * want, while taking 0 bytes of data. */
+       fd = creat(testfile, S_IRWXU);
+       ASSERTF(fd >= 0, "create failed for '%s': %s",
+               testfile, strerror(errno));
+
+       rc = ftruncate(fd, length);
+       ASSERTF(rc == 0, "ftruncate failed for '%s': %s",
+               testfile, strerror(errno));
+
+       return fd;
+}
+
+/* Test llapi_hsm_state_get. */
+void test50(void)
+{
+       struct hsm_user_state hus;
+       int rc;
+       int fd;
+
+       fd = create_testfile(100);
+
+       /* With fd variant */
+       rc = llapi_hsm_state_get_fd(fd, &hus);
+       ASSERTF(rc == 0, "llapi_hsm_state_get_fd failed: %s", strerror(-rc));
+       ASSERTF(hus.hus_states == 0, "state=%u", hus.hus_states);
+
+       rc = llapi_hsm_state_get_fd(fd, NULL);
+       ASSERTF(rc == -EFAULT, "llapi_hsm_state_get_fd error: %s",
+               strerror(-rc));
+
+       rc = close(fd);
+       ASSERTF(rc == 0, "close failed: %s", strerror(errno));
+
+       /* Without fd */
+       rc = llapi_hsm_state_get(testfile, &hus);
+       ASSERTF(rc == 0, "llapi_hsm_state_get failed: %s", strerror(-rc));
+       ASSERTF(hus.hus_states == 0, "state=%u", hus.hus_states);
+
+       rc = llapi_hsm_state_get(testfile, NULL);
+       ASSERTF(rc == -EFAULT, "llapi_hsm_state_get error: %s",
+               strerror(-rc));
+
+       memset(&hus, 0xaa, sizeof(hus));
+       rc = llapi_hsm_state_get(testfile, &hus);
+       ASSERTF(rc == 0, "llapi_hsm_state_get failed: %s", strerror(-rc));
+       ASSERTF(hus.hus_states == 0, "state=%u", hus.hus_states);
+       ASSERTF(hus.hus_archive_id == 0, "archive_id=%u", hus.hus_archive_id);
+       ASSERTF(hus.hus_in_progress_state == 0, "hus_in_progress_state=%u",
+               hus.hus_in_progress_state);
+       ASSERTF(hus.hus_in_progress_action == 0, "hus_in_progress_action=%u",
+               hus.hus_in_progress_action);
+}
+
+/* Test llapi_hsm_state_set. */
+void test51(void)
+{
+       int rc;
+       int fd;
+       int i;
+       struct hsm_user_state hus;
+
+       fd = create_testfile(100);
+
+       rc = llapi_hsm_state_set_fd(fd, 0, 0, 0);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       /* Set archive id */
+       for (i = 0; i <= 32; i++) {
+               rc = llapi_hsm_state_set_fd(fd, HS_EXISTS, 0, i);
+               ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s",
+                       strerror(-rc));
+
+               rc = llapi_hsm_state_get_fd(fd, &hus);
+               ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s",
+                       strerror(-rc));
+               ASSERTF(hus.hus_states == HS_EXISTS, "state=%u",
+                       hus.hus_states);
+               ASSERTF(hus.hus_archive_id == i, "archive_id=%u, i=%d",
+                       hus.hus_archive_id, i);
+       }
+
+       /* Bugs following. This should not succeed. Builds the following file:
+        *
+        *   $ ../utils/lfs hsm_state /mnt/lustre/hsm_check_test
+        *
+        *   /mnt/lustre/hsm_check_test: (0x8008007d) released exists
+        *     archived never_release never_archive lost_from_hsm,
+        *     archive_id:-1789
+        */
+
+       /* Invalid archive numbers */
+       rc = llapi_hsm_state_set_fd(fd, HS_EXISTS, 0, 33);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       rc = llapi_hsm_state_set_fd(fd, HS_EXISTS, 0, 151);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       rc = llapi_hsm_state_set_fd(fd, HS_EXISTS, 0, -1789);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       /* Setable + Unsettable flags */
+       rc = llapi_hsm_state_set_fd(fd, HS_DIRTY, 0, 0);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       rc = llapi_hsm_state_set_fd(fd, 0, HS_DIRTY, 0);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       rc = llapi_hsm_state_set_fd(fd, HS_ARCHIVED, 0, 0);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       rc = llapi_hsm_state_set_fd(fd, HS_RELEASED, 0, 0);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       rc = llapi_hsm_state_set_fd(fd, HS_NORELEASE, 0, 0);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       rc = llapi_hsm_state_set_fd(fd, HS_NOARCHIVE, 0, 0);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       rc = llapi_hsm_state_set_fd(fd, HS_LOST, 0, 0);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       /* Bogus flags for good measure. */
+       rc = llapi_hsm_state_set_fd(fd, 0x00080000, 0, 0);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       rc = llapi_hsm_state_set_fd(fd, 0x80000000, 0, 0);
+       ASSERTF(rc == 0, "llapi_hsm_state_set_fd failed: %s", strerror(-rc));
+
+       close(fd);
+}
+
+/* Test llapi_hsm_current_action */
+void test52(void)
+{
+       int rc;
+       int fd;
+       struct hsm_current_action hca;
+
+       /* No fd equivalent, so close it. */
+       fd = create_testfile(100);
+       close(fd);
+
+       rc = llapi_hsm_current_action(testfile, &hca);
+       ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s", strerror(-rc));
+       ASSERTF(hca.hca_state, "hca_state=%u", hca.hca_state);
+       ASSERTF(hca.hca_action, "hca_state=%u", hca.hca_action);
+
+       rc = llapi_hsm_current_action(testfile, NULL);
+       ASSERTF(rc == -EFAULT, "llapi_hsm_current_action failed: %s",
+               strerror(-rc));
+}
+
+/* Helper to simulate archiving a file. No actual data movement
+ * happens. */
+void (*helper_progress)(struct hsm_copyaction_private *hcp);
+void helper_archiving(const size_t length)
+{
+       int rc;
+       int fd;
+       struct hsm_copytool_private *ctdata;
+       struct hsm_user_request *hur;
+       struct hsm_action_list  *hal;
+       struct hsm_action_item  *hai;
+       int                      msgsize;
+       struct hsm_copyaction_private *hcp;
+       struct hsm_user_state hus;
+
+       fd = create_testfile(length);
+
+       rc = llapi_hsm_copytool_register(&ctdata, fsmountdir,
+                                        0, NULL, 0);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_register failed: %s",
+               strerror(-rc));
+
+       /* Create and send the archive request. */
+       hur = llapi_hsm_user_request_alloc(1, 0);
+       ASSERTF(hur != NULL, "llapi_hsm_user_request_alloc returned NULL");
+
+       hur->hur_request.hr_action = HUA_ARCHIVE;
+       hur->hur_request.hr_archive_id = 1;
+       hur->hur_request.hr_flags = 0;
+       hur->hur_request.hr_itemcount = 1;
+       hur->hur_request.hr_data_len = 0;
+       hur->hur_user_item[0].hui_extent.length = -1;
+
+       rc = llapi_fd2fid(fd, &hur->hur_user_item[0].hui_fid);
+       ASSERTF(rc == 0, "llapi_fd2fid failed: %s", strerror(-rc));
+
+       close(fd);
+
+       rc = llapi_hsm_request(testfile, hur);
+       ASSERTF(rc == 0, "llapi_hsm_request failed: %s", strerror(-rc));
+
+       free(hur);
+
+       /* Read the request */
+       rc = llapi_hsm_copytool_recv(ctdata, &hal, &msgsize);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_recv failed: %s", strerror(-rc));
+       ASSERTF(hal->hal_count == 1, "hal_count=%d", hal->hal_count);
+
+       hai = hai_first(hal);
+       ASSERTF(hai != NULL, "hai_first returned NULL");
+       ASSERTF(hai->hai_action == HSMA_ARCHIVE,
+               "hai_action=%d", hai->hai_action);
+
+       /* "Begin" archiving */
+       hcp = NULL;
+       rc = llapi_hsm_action_begin(&hcp, ctdata, hai, -1, 0, false);
+       ASSERTF(rc == 0, "llapi_hsm_action_begin failed: %s", strerror(-rc));
+       ASSERTF(hcp != NULL, "hcp is NULL");
+
+       if (helper_progress)
+               helper_progress(hcp);
+
+       /* Done archiving */
+       rc = llapi_hsm_action_end(&hcp, &hai->hai_extent, 0, 0);
+       ASSERTF(rc == 0, "llapi_hsm_action_end failed: %s", strerror(-rc));
+       ASSERTF(hcp == NULL, "hcp is NULL");
+
+       /* Close HSM client */
+       rc = llapi_hsm_copytool_unregister(&ctdata);
+       ASSERTF(rc == 0, "llapi_hsm_copytool_unregister failed: %s",
+               strerror(-rc));
+
+       /* Final check */
+       rc = llapi_hsm_state_get(testfile, &hus);
+       ASSERTF(rc == 0, "llapi_hsm_state_get failed: %s", strerror(-rc));
+       ASSERTF(hus.hus_states == (HS_EXISTS | HS_ARCHIVED),
+               "state=%u", hus.hus_states);
+}
+
+/* Simple archive. No progress. */
+void test100(void)
+{
+       const size_t length = 100;
+       helper_progress = NULL;
+       helper_archiving(length);
+}
+
+/* Archive, with a report every byte. */
+void test101(void)
+{
+       const size_t length = 1000;
+
+       void test101_progress(struct hsm_copyaction_private *hcp)
+       {
+               int i;
+               int rc;
+               struct hsm_extent he;
+               struct hsm_current_action hca;
+
+               /* Report progress. 1 byte at a time :) */
+               for (i = 0; i < length; i++) {
+                       he.offset = i;
+                       he.length = 1;
+                       rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+                       ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                               strerror(-rc));
+               }
+
+               rc = llapi_hsm_current_action(testfile, &hca);
+               ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                       strerror(-rc));
+               ASSERTF(hca.hca_state == HPS_RUNNING,
+                       "hca_state=%u", hca.hca_state);
+               ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                       "hca_state=%u", hca.hca_action);
+               ASSERTF(hca.hca_location.length == length,
+                       "length=%llu", hca.hca_location.length);
+       }
+
+       helper_progress = test101_progress;
+       helper_archiving(length);
+}
+
+/* Archive, with a report every byte, backwards. */
+void test102(void)
+{
+       const size_t length = 1000;
+
+       void test102_progress(struct hsm_copyaction_private *hcp)
+       {
+               int i;
+               int rc;
+               struct hsm_extent he;
+               struct hsm_current_action hca;
+
+               /* Report progress. 1 byte at a time :) */
+               for (i = length-1; i >= 0; i--) {
+                       he.offset = i;
+                       he.length = 1;
+                       rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+                       ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                               strerror(-rc));
+               }
+
+               rc = llapi_hsm_current_action(testfile, &hca);
+               ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                       strerror(-rc));
+               ASSERTF(hca.hca_state == HPS_RUNNING,
+                       "hca_state=%u", hca.hca_state);
+               ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                       "hca_state=%u", hca.hca_action);
+               ASSERTF(hca.hca_location.length == length,
+                       "length=%llu", hca.hca_location.length);
+       }
+
+       helper_progress = test102_progress;
+       helper_archiving(length);
+}
+
+/* Archive, with a single report. */
+void test103(void)
+{
+       const size_t length = 1000;
+
+       void test103_progress(struct hsm_copyaction_private *hcp)
+       {
+               int rc;
+               struct hsm_extent he;
+               struct hsm_current_action hca;
+
+               he.offset = 0;
+               he.length = length;
+               rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+               ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                       strerror(-rc));
+
+               rc = llapi_hsm_current_action(testfile, &hca);
+               ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                       strerror(-rc));
+               ASSERTF(hca.hca_state == HPS_RUNNING,
+                       "hca_state=%u", hca.hca_state);
+               ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                       "hca_state=%u", hca.hca_action);
+               ASSERTF(hca.hca_location.length == length,
+                       "length=%llu", hca.hca_location.length);
+       }
+
+       helper_progress = test103_progress;
+       helper_archiving(length);
+}
+
+/* Archive, with 2 reports. */
+void test104(void)
+{
+       const size_t length = 1000;
+
+       void test104_progress(struct hsm_copyaction_private *hcp)
+       {
+               int rc;
+               struct hsm_extent he;
+               struct hsm_current_action hca;
+
+               he.offset = 0;
+               he.length = length/2;
+               rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+               ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                       strerror(-rc));
+
+               he.offset = length/2;
+               he.length = length/2;
+               rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+               ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                       strerror(-rc));
+
+               rc = llapi_hsm_current_action(testfile, &hca);
+               ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                       strerror(-rc));
+               ASSERTF(hca.hca_state == HPS_RUNNING,
+                       "hca_state=%u", hca.hca_state);
+               ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                       "hca_state=%u", hca.hca_action);
+               ASSERTF(hca.hca_location.length == length,
+                       "length=%llu", hca.hca_location.length);
+       }
+
+       helper_progress = test104_progress;
+       helper_archiving(length);
+}
+
+/* Archive, with 1 bogus report. */
+void test105(void)
+{
+       const size_t length = 1000;
+
+       void test105_progress(struct hsm_copyaction_private *hcp)
+       {
+               int rc;
+               struct hsm_extent he;
+               struct hsm_current_action hca;
+
+               he.offset = 2*length;
+               he.length = 10*length;
+               rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+               ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                       strerror(-rc));
+
+               rc = llapi_hsm_current_action(testfile, &hca);
+               ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                       strerror(-rc));
+               ASSERTF(hca.hca_state == HPS_RUNNING,
+                       "hca_state=%u", hca.hca_state);
+               ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                       "hca_state=%u", hca.hca_action);
+
+               /* BUG - offset should be 2*length, or length should
+                * be 8*length */
+               ASSERTF(hca.hca_location.length == 10*length,
+                       "length=%llu", hca.hca_location.length);
+       }
+
+       helper_progress = test105_progress;
+       helper_archiving(length);
+}
+
+/* Archive, with 1 empty report. */
+void test106(void)
+{
+       const size_t length = 1000;
+
+       void test106_progress(struct hsm_copyaction_private *hcp)
+       {
+               int rc;
+               struct hsm_extent he;
+               struct hsm_current_action hca;
+
+               he.offset = 0;
+               he.length = 0;
+               rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+               ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                       strerror(-rc));
+
+               rc = llapi_hsm_current_action(testfile, &hca);
+               ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                       strerror(-rc));
+               ASSERTF(hca.hca_state == HPS_RUNNING,
+                       "hca_state=%u", hca.hca_state);
+               ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                       "hca_state=%u", hca.hca_action);
+               ASSERTF(hca.hca_location.length == 0,
+                       "length=%llu", hca.hca_location.length);
+       }
+
+       helper_progress = test106_progress;
+       helper_archiving(length);
+}
+
+/* Archive, with 1 bogus report. */
+void test107(void)
+{
+       const size_t length = 1000;
+
+       void test107_progress(struct hsm_copyaction_private *hcp)
+       {
+               int rc;
+               struct hsm_extent he;
+               struct hsm_current_action hca;
+
+               he.offset = -1;
+               he.length = 10;
+               rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+               ASSERTF(rc == -EINVAL, "llapi_hsm_action_progress error: %s",
+                       strerror(-rc));
+
+               rc = llapi_hsm_current_action(testfile, &hca);
+               ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                       strerror(-rc));
+               ASSERTF(hca.hca_state == HPS_RUNNING,
+                       "hca_state=%u", hca.hca_state);
+               ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                       "hca_state=%u", hca.hca_action);
+               ASSERTF(hca.hca_location.length == 0,
+                       "length=%llu", hca.hca_location.length);
+       }
+
+       helper_progress = test107_progress;
+       helper_archiving(length);
+}
+
+/* Archive, with same report, many times. */
+void test108(void)
+{
+       const size_t length = 1000;
+
+       void test108_progress(struct hsm_copyaction_private *hcp)
+       {
+               int rc;
+               struct hsm_extent he;
+               int i;
+               struct hsm_current_action hca;
+
+               for (i = 0; i < 1000; i++) {
+                       he.offset = 0;
+                       he.length = length;
+                       rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+                       ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                               strerror(-rc));
+               }
+
+               rc = llapi_hsm_current_action(testfile, &hca);
+               ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                       strerror(-rc));
+               ASSERTF(hca.hca_state == HPS_RUNNING,
+                       "hca_state=%u", hca.hca_state);
+               ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                       "hca_state=%u", hca.hca_action);
+               ASSERTF(hca.hca_location.length == length,
+                       "length=%llu", hca.hca_location.length);
+       }
+
+       helper_progress = test108_progress;
+       helper_archiving(length);
+}
+
+/* Archive, 1 report, with large number. */
+void test109(void)
+{
+       const size_t length = 1000;
+
+       void test109_progress(struct hsm_copyaction_private *hcp)
+       {
+               int rc;
+               struct hsm_extent he;
+               struct hsm_current_action hca;
+
+               he.offset = 0;
+               he.length = 0xffffffffffffffffULL;
+               rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+               ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                       strerror(-rc));
+
+               rc = llapi_hsm_current_action(testfile, &hca);
+               ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                       strerror(-rc));
+               ASSERTF(hca.hca_state == HPS_RUNNING,
+                       "hca_state=%u", hca.hca_state);
+               ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                       "hca_state=%u", hca.hca_action);
+               ASSERTF(hca.hca_location.length == 0xffffffffffffffffULL,
+                       "length=%llu", hca.hca_location.length);
+       }
+
+       helper_progress = test109_progress;
+       helper_archiving(length);
+}
+
+/* Archive, with 10 reports, checking progress. */
+void test110(void)
+{
+       const size_t length = 1000;
+
+       void test110_progress(struct hsm_copyaction_private *hcp)
+       {
+               int rc;
+               int i;
+               struct hsm_extent he;
+               struct hsm_current_action hca;
+
+               for (i = 0; i < 10; i++) {
+                       he.offset = i*length/10;
+                       he.length = length/10;
+                       rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+                       ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                               strerror(-rc));
+
+                       rc = llapi_hsm_current_action(testfile, &hca);
+                       ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                               strerror(-rc));
+                       ASSERTF(hca.hca_state == HPS_RUNNING,
+                               "hca_state=%u", hca.hca_state);
+                       ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                               "hca_state=%u", hca.hca_action);
+                       ASSERTF(hca.hca_location.length == (i+1)*length/10,
+                               "i=%d, length=%llu",
+                               i, hca.hca_location.length);
+               }
+       }
+
+       helper_progress = test110_progress;
+       helper_archiving(length);
+}
+
+/* Archive, with 10 reports in reverse order, checking progress. */
+void test111(void)
+{
+       const size_t length = 1000;
+
+       void test111_progress(struct hsm_copyaction_private *hcp)
+       {
+               int rc;
+               int i;
+               struct hsm_extent he;
+               struct hsm_current_action hca;
+
+               for (i = 0; i < 10; i++) {
+                       he.offset = (9-i)*length/10;
+                       he.length = length/10;
+                       rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+                       ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                               strerror(-rc));
+
+                       rc = llapi_hsm_current_action(testfile, &hca);
+                       ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                               strerror(-rc));
+                       ASSERTF(hca.hca_state == HPS_RUNNING,
+                               "hca_state=%u", hca.hca_state);
+                       ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                               "hca_state=%u", hca.hca_action);
+                       ASSERTF(hca.hca_location.length == (i+1)*length/10,
+                               "i=%d, length=%llu",
+                               i, hca.hca_location.length);
+               }
+       }
+
+       helper_progress = test111_progress;
+       helper_archiving(length);
+}
+
+/* Archive, with 10 reports, and duplicating them, checking
+ * progress. */
+void test112(void)
+{
+       const size_t length = 1000;
+
+       void test112_progress(struct hsm_copyaction_private *hcp)
+       {
+               int rc;
+               int i;
+               struct hsm_extent he;
+               struct hsm_current_action hca;
+
+               for (i = 0; i < 10; i++) {
+                       he.offset = i*length/10;
+                       he.length = length/10;
+                       rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+                       ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                               strerror(-rc));
+
+                       rc = llapi_hsm_current_action(testfile, &hca);
+                       ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                               strerror(-rc));
+                       ASSERTF(hca.hca_state == HPS_RUNNING,
+                               "hca_state=%u", hca.hca_state);
+                       ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                               "hca_state=%u", hca.hca_action);
+                       ASSERTF(hca.hca_location.length == (i+1)*length/10,
+                               "i=%d, length=%llu",
+                               i, hca.hca_location.length);
+               }
+
+               for (i = 0; i < 10; i++) {
+                       he.offset = i*length/10;
+                       he.length = length/10;
+                       rc = llapi_hsm_action_progress(hcp, &he, length, 0);
+                       ASSERTF(rc == 0, "llapi_hsm_action_progress failed: %s",
+                               strerror(-rc));
+
+                       rc = llapi_hsm_current_action(testfile, &hca);
+                       ASSERTF(rc == 0, "llapi_hsm_current_action failed: %s",
+                               strerror(-rc));
+                       ASSERTF(hca.hca_state == HPS_RUNNING,
+                               "hca_state=%u", hca.hca_state);
+                       ASSERTF(hca.hca_action == HUA_ARCHIVE,
+                               "hca_state=%u", hca.hca_action);
+                       ASSERTF(hca.hca_location.length == length,
+                               "i=%d, length=%llu",
+                               i, hca.hca_location.length);
+               }
+
+       }
+
+       helper_progress = test112_progress;
+       helper_archiving(length);
+}
+
+static void usage(char *prog)
+{
+       fprintf(stderr, "Usage: %s [-d lustre_dir]\n", prog);
+       exit(EXIT_FAILURE);
+}
+
+static void process_args(int argc, char *argv[])
+{
+       int c;
+
+       while ((c = getopt(argc, argv, "d:")) != -1) {
+               switch (c) {
+               case 'd':
+                       lustre_dir = optarg;
+                       break;
+               case '?':
+               default:
+                       fprintf(stderr, "Unknown option '%c'\n", optopt);
+                       usage(argv[0]);
+                       break;
+               }
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       char fsname[8];
+       int rc;
+
+       process_args(argc, argv);
+       if (lustre_dir == NULL)
+               lustre_dir = "/mnt/lustre";
+
+       rc = llapi_search_mounts(lustre_dir, 0, fsmountdir, fsname);
+       if (rc != 0) {
+               fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
+                       lustre_dir);
+               return EXIT_FAILURE;
+       }
+
+       /* Play nice with Lustre test scripts. Non-line buffered output
+        * stream under I/O redirection may appear incorrectly. */
+       setvbuf(stdout, NULL, _IOLBF, 0);
+
+       PERFORM(test1);
+       PERFORM(test2);
+       PERFORM(test3);
+       PERFORM(test4);
+       PERFORM(test5);
+       PERFORM(test6);
+       PERFORM(test7);
+       PERFORM(test50);
+       PERFORM(test51);
+       PERFORM(test52);
+       PERFORM(test100);
+       PERFORM(test101);
+       PERFORM(test102);
+       PERFORM(test103);
+       PERFORM(test104);
+       PERFORM(test105);
+       PERFORM(test106);
+       PERFORM(test107);
+       PERFORM(test108);
+       PERFORM(test109);
+       PERFORM(test110);
+       PERFORM(test111);
+       PERFORM(test112);
+
+       return EXIT_SUCCESS;
+}
index 28a8d02..3624f30 100755 (executable)
@@ -4261,6 +4261,19 @@ test_405() {
 }
 run_test 405 "archive and release under striped directory"
 
+test_500()
+{
+       [ $(lustre_version_code $SINGLEMDS) -lt $(version_code 2.6.92) ] &&
+               skip "HSM migrate is not supported" && return
+
+       # Stop the existing copytool
+       copytool_cleanup
+
+       test_mkdir -p $DIR/$tdir
+       llapi_hsm_test -d $DIR/$tdir || error "One llapi HSM test failed"
+}
+run_test 500 "various LLAPI HSM tests"
+
 copytool_cleanup
 
 complete $SECONDS