Whamcloud - gitweb
LU-15762 tests: skip llapi_hsm_test113 on old server 86/58786/10
authorAndreas Dilger <adilger@whamcloud.com>
Mon, 14 Apr 2025 23:31:09 +0000 (17:31 -0600)
committerOleg Drokin <green@whamcloud.com>
Fri, 2 May 2025 02:21:02 +0000 (02:21 +0000)
Skip sanity-hsm test_500 sub-subtest 113 for old server interop.

This requires adding some infrastructure to allow skipping some
sub-subtests in llapi_hsm_test.c. Moved from llapi_layout_test.c
to llapi_test_utils.c and enhanced to allow non-contiguous test
numbering and sharing between test files.

Modify llapi_layout_test.c to include these same improvements.

Update code style in sanity-hsm test_500 and sanity test_27D so
both modified test programs run in review-subtest-change sessions.

Fix version check in sanity/27D that always skipped sub-subtest 34.

Test-Parameters: trivial testlist=sanity-hsm env=ONLY=500 serverversion=2.14
Fixes: f684172237 ("LU-11085 mdt: revise recording of hsm progress updates.")
Signed-off-by: Andreas Dilger <adilger@whamcloud.com>
Change-Id: I1d234632444404346142e45e74b61ffbe6500c1e
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/58786
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Jian Yu <yujian@whamcloud.com>
Reviewed-by: Rajeev Mishra <rajeevm@hpe.com>
Reviewed-by: Nikitas Angelinas <nikitas.angelinas@hpe.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/tests/Makefile.am
lustre/tests/llapi_hsm_test.c
lustre/tests/llapi_layout_test.c
lustre/tests/llapi_test_utils.c [new file with mode: 0644]
lustre/tests/llapi_test_utils.h [new file with mode: 0644]
lustre/tests/sanity-hsm.sh
lustre/tests/sanity.sh

index 9c8d166..47c55c0 100644 (file)
@@ -108,7 +108,9 @@ endif # NO_STRINGOP_OVERFLOW
 
 mmap_sanity_LDADD = $(LIBLUSTREAPI) $(PTHREAD_LIBS)
 multiop_LDADD = $(LIBLUSTREAPI) $(PTHREAD_LIBS)
+llapi_layout_test_SOURCES = llapi_layout_test.c llapi_test_utils.c llapi_test_utils.h
 llapi_layout_test_LDADD = $(LIBLUSTREAPI)
+llapi_hsm_test_SOURCES = llapi_hsm_test.c llapi_test_utils.c llapi_test_utils.h
 llapi_hsm_test_LDADD = $(LIBLUSTREAPI)
 group_lock_test_LDADD = $(LIBLUSTREAPI)
 llapi_fid_test_LDADD = $(LIBLUSTREAPI)
index 890fb4a..e5a3364 100644 (file)
@@ -1,85 +1,33 @@
-/*
- * 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
- */
+// SPDX-License-Identifier: GPL-2.0
 
 /*
  * Copyright 2014, 2015 Cray Inc, all rights reserved.
- *
  * Copyright (c) 2015, Intel Corporation.
- */
-/* 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:
- *   lctl set_param mdt.$FSNAME-MDT0000.hsm_control=enabled
+ * Copyright (c) 2025, DataDirect Networks, Inc. All rights reserved.
  */
 
-/* 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 <signal.h>
+#include <string.h>
+#include <unistd.h>
 
 #include <lustre/lustreapi.h>
+#include "llapi_test_utils.h"
 
-static char fsmountdir[PATH_MAX];      /* Lustre mountpoint */
-static char *lustre_dir;               /* Test directory inside Lustre */
-static bool is_bitmap;
-
-#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.
- */
-static int test1(void)
+static bool is_bitmap;         /* use old bitmap interface */
+static char lustre_dir[PATH_MAX - 5];  /* Lustre test directory */
+
+static void usage(char *prog)
+{
+       printf("Usage: %s [-d LUSTRE_DIR] [-s SKIP[,SKIP...]] [-t ONLY[,ONLY...]\n",
+              prog);
+       exit(0);
+}
+
+#define T1_DESC                "Register/unregister copytool 2000x to check for leaks"
+static void test1(void)
 {
        int i;
        int rc;
@@ -97,12 +45,10 @@ static int test1(void)
                        "llapi_hsm_copytool_unregister failed: %s, loop=%d",
                        strerror(-rc), i);
        }
-
-       return 0;
 }
 
-/* Re-register */
-static int test2(void)
+#define T2_DESC                "Re/un-register copytool multiple times without error"
+static void test2(void)
 {
        int rc;
        struct hsm_copytool_private *ctdata1;
@@ -123,12 +69,10 @@ static int test2(void)
        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(). */
-static int test3(void)
+#define T3_DESC                "Pass bad parameters to llapi_hsm_copytool_register()"
+static void test3(void)
 {
        int rc;
        struct hsm_copytool_private *ctdata;
@@ -169,24 +113,20 @@ static int test3(void)
        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(). */
-static int test4(void)
+#define T4_DESC                "Bad parameters to llapi_hsm_copytool_unregister()"
+static void 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 */
-static int test5(void)
+#define T5_DESC                "Test llapi_hsm_copytool_recv() in non-blocking mode"
+static void test5(void)
 {
        int rc;
        int i;
@@ -209,12 +149,10 @@ static int test5(void)
        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 */
-static int test6(void)
+#define T6_DESC                "Test llapi_hsm_copytool_recv() with bogus parameters"
+static void test6(void)
 {
        struct hsm_copytool_private *ctdata;
        struct hsm_action_list *hal;
@@ -244,12 +182,10 @@ static int test6(void)
        rc = llapi_hsm_copytool_unregister(&ctdata);
        ASSERTF(rc == 0, "llapi_hsm_copytool_unregister failed: %s",
                strerror(-rc));
-
-       return 0;
 }
 
-/* Test polling (without actual traffic) */
-static int test7(void)
+#define T7_DESC                "Test event polling (without actual traffic)"
+static void test7(void)
 {
        int rc;
        struct hsm_copytool_private *ctdata;
@@ -285,8 +221,6 @@ static int test7(void)
        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. */
@@ -317,7 +251,7 @@ static int create_testfile(size_t length)
        return fd;
 }
 
-/* Test llapi_hsm_state_get. */
+#define T50_DESC               "Test llapi_hsm_state_get()/get_fd()"
 static void test50(void)
 {
        struct hsm_user_state hus;
@@ -358,7 +292,7 @@ static void test50(void)
                hus.hus_in_progress_action);
 }
 
-/* Test llapi_hsm_state_set. */
+#define T51_DESC       "Test llapi_hsm_state_set_fd()"
 static void test51(void)
 {
        int rc;
@@ -488,7 +422,7 @@ static void test51(void)
        close(fd);
 }
 
-/* Test llapi_hsm_current_action */
+#define T52_DESC       "Test llapi_hsm_current_action()"
 static void test52(void)
 {
        int rc;
@@ -589,7 +523,7 @@ static void helper_archiving(void (*progress)
                "state=%u", hus.hus_states);
 }
 
-/* Simple archive. No progress. */
+#define T100_DESC      "Simple archive creation with no progress reported."
 static void test100(void)
 {
        const size_t length = 100;
@@ -597,7 +531,7 @@ static void test100(void)
        helper_archiving(NULL, length);
 }
 
-/* Archive, with a report every byte. */
+#define T101_DESC      "Simple archive creation with progress every byte."
 static void test101_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int i;
@@ -632,7 +566,7 @@ static void test101(void)
        helper_archiving(test101_progress, length);
 }
 
-/* Archive, with a report every byte, backwards. */
+#define T102_DESC      "Archive creation with progress every byte, backwards"
 static void test102_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int i;
@@ -667,7 +601,7 @@ static void test102(void)
        helper_archiving(test102_progress, length);
 }
 
-/* Archive, with a single report. */
+#define T103_DESC      "Archive creation with a single progress report"
 static void test103_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int rc;
@@ -698,7 +632,7 @@ static void test103(void)
        helper_archiving(test103_progress, length);
 }
 
-/* Archive, with 2 reports. */
+#define T104_DESC      "Archive creation with two progress reports"
 static void test104_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int rc;
@@ -735,7 +669,7 @@ static void test104(void)
        helper_archiving(test104_progress, length);
 }
 
-/* Archive, with 1 bogus report. */
+#define T105_DESC      "Archive creation with one bogus progress report"
 static void test105_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int rc;
@@ -768,7 +702,7 @@ static void test105(void)
        helper_archiving(test105_progress, length);
 }
 
-/* Archive, with 1 empty report. */
+#define T106_DESC      "Archive creation with one empty progress report"
 static void test106_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int rc;
@@ -799,7 +733,7 @@ static void test106(void)
        helper_archiving(test106_progress, length);
 }
 
-/* Archive, with 1 bogus report. */
+#define T107_DESC      "Archive creation with one bogus progress report"
 static void test107_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int rc;
@@ -830,7 +764,7 @@ static void test107(void)
        helper_archiving(test107_progress, length);
 }
 
-/* Archive, with same report, many times. */
+#define T108_DESC      "Archive creation with same progress report each time"
 static void test108_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int rc;
@@ -864,7 +798,7 @@ static void test108(void)
        helper_archiving(test108_progress, length);
 }
 
-/* Archive, 1 report, with large number. */
+#define T109_DESC      "Archive creation with one report, with large number"
 static void test109_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int rc;
@@ -895,7 +829,7 @@ static void test109(void)
        helper_archiving(test109_progress, length);
 }
 
-/* Archive, with 10 reports, checking progress. */
+#define T110_DESC      "Archive with 10 progress reports, checking progress"
 static void test110_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int rc;
@@ -930,7 +864,7 @@ static void test110(void)
        helper_archiving(test110_progress, length);
 }
 
-/* Archive, with 10 reports in reverse order, checking progress. */
+#define T111_DESC      "Archive with 10 reports in reverse, checking progress"
 static void test111_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int rc;
@@ -965,7 +899,7 @@ static void test111(void)
        helper_archiving(test111_progress, length);
 }
 
-/* Archive, with 10 reports, and duplicating them, checking progress. */
+#define T112_DESC      "Archive with 10 reports, duplicated, check progress"
 static void test112_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int rc;
@@ -1019,7 +953,7 @@ static void test112(void)
        helper_archiving(test112_progress, length);
 }
 
-/* Archive, with 9 reports, each covering 20%, so many overlap. */
+#define T113_DESC      "Archive with 9 reports, with 20% overlapping coverage"
 static void test113_progress(struct hsm_copyaction_private *hcp, size_t length)
 {
        int rc;
@@ -1054,23 +988,52 @@ static void test113(void)
        helper_archiving(test113_progress, length);
 }
 
-static void usage(char *prog)
-{
-       fprintf(stderr, "Usage: %s [-d lustre_dir]\n", prog);
-       exit(EXIT_FAILURE);
-}
+static struct test_tbl_entry test_tbl[] = {
+       TEST_REGISTER(1),
+       TEST_REGISTER(2),
+       TEST_REGISTER(3),
+       TEST_REGISTER(4),
+       TEST_REGISTER(5),
+       TEST_REGISTER(6),
+       TEST_REGISTER(7),
+       TEST_REGISTER(50),
+       TEST_REGISTER(51),
+       TEST_REGISTER(52),
+       TEST_REGISTER(100),
+       TEST_REGISTER(101),
+       TEST_REGISTER(102),
+       TEST_REGISTER(103),
+       TEST_REGISTER(104),
+       TEST_REGISTER(105),
+       TEST_REGISTER(106),
+       TEST_REGISTER(107),
+       TEST_REGISTER(108),
+       TEST_REGISTER(109),
+       TEST_REGISTER(110),
+       TEST_REGISTER(111),
+       TEST_REGISTER(112),
+       TEST_REGISTER(113),
+       TEST_REGISTER_END
+};
 
 static void process_args(int argc, char *argv[])
 {
        int c;
 
-       while ((c = getopt(argc, argv, "bd:")) != -1) {
+       while ((c = getopt(argc, argv, "bd:s:t:")) != -1) {
                switch (c) {
-               case 'd':
-                       lustre_dir = optarg;
-                       break;
                case 'b':
                        is_bitmap = true;
+               case 'd':
+                       if (snprintf(lustre_dir, sizeof(lustre_dir), "%s",
+                                    optarg) >= sizeof(lustre_dir))
+                               DIE("Error: test directory name too long\n");
+                       break;
+               case 's':
+                       set_tests_to_skip(optarg, test_tbl);
+                       break;
+               case 't':
+                       set_tests_to_run(optarg, test_tbl);
                        break;
                case '?':
                default:
@@ -1083,49 +1046,7 @@ static void process_args(int argc, char *argv[])
 
 int main(int argc, char *argv[])
 {
-       char fsname[8 + 1];
-       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);
-       PERFORM(test113);
-
-       return EXIT_SUCCESS;
+       return run_tests(lustre_dir, test_tbl);
 }
index 0e16f1b..b4d45a0 100644 (file)
@@ -1,26 +1,7 @@
-/*
- * 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
- */
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2016, 2017, Intel Corporation.
+ * Copyright (c) 2025, DataDirect Networks, Inc. All rights reserved.
  */
 /*
  * These tests exercise the llapi_layout API which abstracts the layout
  *  sudo ./llapi_layout_test
  */
 
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <fcntl.h>
-#include <sys/wait.h>
-#include <sys/signal.h>
-#include <sys/types.h>
 #include <errno.h>
-#include <lustre/lustreapi.h>
-#include <pwd.h>
-#include <limits.h>
-#include <sys/stat.h>
+#include <fcntl.h>
 #include <getopt.h>
 #include <inttypes.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdint.h>
 #include <sys/ioctl.h>
+#include <sys/types.h>
 
-#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)
+#include <lustre/lustreapi.h>
+#include "llapi_test_utils.h"
 
-#define ASSERTF(cond, fmt, ...)                                                \
-do {                                                                   \
-       if (!(cond))                                                    \
-               DIE("assertion '%s' failed: "fmt, #cond, ## __VA_ARGS__);\
-} while (0)                                                            \
+static char *poolname = "testpool";
+static int num_osts = 2;
+static char lustre_dir[PATH_MAX - 5];  /* Lustre test directory */
 
 #define IN_RANGE(value, low, high) ((value >= low) && (value <= high))
 
-static char *lustre_dir;
-static char *poolname;
-static bool run_list_provided;
-static int num_osts = -1;
-
 static void usage(char *prog)
 {
        printf("Usage: %s [-d lustre_dir] [-p pool_name] [-o num_osts] "
@@ -1751,155 +1710,45 @@ static void test34(void)
        ASSERTF(rc == 0, "errno %d", errno);
 }
 
-#define TEST_DESC_LEN  80
-struct test_tbl_entry {
-       void (*tte_fn)(void);
-       char tte_desc[TEST_DESC_LEN];
-       bool tte_skip;
-};
-
 static struct test_tbl_entry test_tbl[] = {
-       { .tte_fn = &test0, .tte_desc = T0_DESC, .tte_skip = false },
-       { .tte_fn = &test1, .tte_desc = T1_DESC, .tte_skip = false },
-       { .tte_fn = &test2, .tte_desc = T2_DESC, .tte_skip = false },
-       { .tte_fn = &test3, .tte_desc = T3_DESC, .tte_skip = false },
-       { .tte_fn = &test4, .tte_desc = T4_DESC, .tte_skip = false },
-       { .tte_fn = &test5, .tte_desc = T5_DESC, .tte_skip = false },
-       { .tte_fn = &test6, .tte_desc = T6_DESC, .tte_skip = false },
-       { .tte_fn = &test7, .tte_desc = T7_DESC, .tte_skip = false },
-       { .tte_fn = &test8, .tte_desc = T8_DESC, .tte_skip = false },
-       { .tte_fn = &test9, .tte_desc = T9_DESC, .tte_skip = false },
-       { .tte_fn = &test10, .tte_desc = T10_DESC, .tte_skip = false },
-       { .tte_fn = &test11, .tte_desc = T11_DESC, .tte_skip = false },
-       { .tte_fn = &test12, .tte_desc = T12_DESC, .tte_skip = false },
-       { .tte_fn = &test13, .tte_desc = T13_DESC, .tte_skip = false },
-       { .tte_fn = &test14, .tte_desc = T14_DESC, .tte_skip = false },
-       { .tte_fn = &test15, .tte_desc = T15_DESC, .tte_skip = false },
-       { .tte_fn = &test16, .tte_desc = T16_DESC, .tte_skip = false },
-       { .tte_fn = &test17, .tte_desc = T17_DESC, .tte_skip = false },
-       { .tte_fn = &test18, .tte_desc = T18_DESC, .tte_skip = false },
-       { .tte_fn = &test19, .tte_desc = T19_DESC, .tte_skip = false },
-       { .tte_fn = &test20, .tte_desc = T20_DESC, .tte_skip = false },
-       { .tte_fn = &test21, .tte_desc = T21_DESC, .tte_skip = false },
-       { .tte_fn = &test22, .tte_desc = T22_DESC, .tte_skip = false },
-       { .tte_fn = &test23, .tte_desc = T23_DESC, .tte_skip = false },
-       { .tte_fn = &test24, .tte_desc = T24_DESC, .tte_skip = false },
-       { .tte_fn = &test25, .tte_desc = T25_DESC, .tte_skip = false },
-       { .tte_fn = &test26, .tte_desc = T26_DESC, .tte_skip = false },
-       { .tte_fn = &test27, .tte_desc = T27_DESC, .tte_skip = false },
-       { .tte_fn = &test28, .tte_desc = T28_DESC, .tte_skip = false },
-       { .tte_fn = &test29, .tte_desc = T29_DESC, .tte_skip = false },
-       { .tte_fn = &test30, .tte_desc = T30_DESC, .tte_skip = false },
-       { .tte_fn = &test31, .tte_desc = T31_DESC, .tte_skip = false },
-       { .tte_fn = &test32, .tte_desc = T32_DESC, .tte_skip = false },
-       { .tte_fn = &test33, .tte_desc = T33_DESC, .tte_skip = false },
-       { .tte_fn = &test34, .tte_desc = T34_DESC, .tte_skip = false },
+       TEST_REGISTER(0),
+       TEST_REGISTER(1),
+       TEST_REGISTER(2),
+       TEST_REGISTER(3),
+       TEST_REGISTER(4),
+       TEST_REGISTER(5),
+       TEST_REGISTER(6),
+       TEST_REGISTER(7),
+       TEST_REGISTER(8),
+       TEST_REGISTER(9),
+       TEST_REGISTER(10),
+       TEST_REGISTER(11),
+       TEST_REGISTER(12),
+       TEST_REGISTER(13),
+       TEST_REGISTER(14),
+       TEST_REGISTER(15),
+       TEST_REGISTER(16),
+       TEST_REGISTER(17),
+       TEST_REGISTER(18),
+       TEST_REGISTER(19),
+       TEST_REGISTER(20),
+       TEST_REGISTER(21),
+       TEST_REGISTER(22),
+       TEST_REGISTER(23),
+       TEST_REGISTER(24),
+       TEST_REGISTER(25),
+       TEST_REGISTER(26),
+       TEST_REGISTER(27),
+       TEST_REGISTER(28),
+       TEST_REGISTER(29),
+       TEST_REGISTER(30),
+       TEST_REGISTER(31),
+       TEST_REGISTER(32),
+       TEST_REGISTER(33),
+       TEST_REGISTER(34),
+       TEST_REGISTER_END
 };
 
-#define NUM_TESTS      (sizeof(test_tbl) / sizeof(struct test_tbl_entry))
-
-static void print_test_desc(int test_num, const char *test_desc,
-                           const char *status)
-{
-       int i;
-
-       printf(" test %2d: %s ", test_num, test_desc);
-       for (i = 0; i < TEST_DESC_LEN - strlen(test_desc); i++)
-               printf(".");
-       printf(" %s\n", status);
-}
-
-/* This function runs a single test by forking the process.  This way,
- * if there is a segfault during a test, the test program won't crash.
- */
-static int test(void (*test_fn)(), const char *test_desc, bool test_skip,
-               int test_num)
-{
-       int rc = 0;
-       pid_t pid;
-       char status_buf[128];
-
-       if (test_skip) {
-               if (!run_list_provided)
-                       print_test_desc(test_num, test_desc, "skip");
-               return 0;
-       }
-
-       pid = fork();
-       if (pid < 0) {
-               ERROR("cannot fork: %s", strerror(errno));
-       } else if (pid > 0) {
-               int status = 0;
-
-               /* Non-zero value indicates failure. */
-               wait(&status);
-               if (status == 0) {
-                       strncpy(status_buf, "pass", sizeof(status_buf));
-               } else if WIFSIGNALED(status) {
-                       snprintf(status_buf, sizeof(status_buf),
-                                "fail (exit status %d, killed by SIG%d)",
-                                WEXITSTATUS(status), WTERMSIG(status));
-                       rc = -1;
-               } else {
-                       snprintf(status_buf, sizeof(status_buf),
-                                "fail (exit status %d)", WEXITSTATUS(status));
-                       rc = -1;
-               }
-               print_test_desc(test_num, test_desc, status_buf);
-       } else if (pid == 0) {
-               /* Run the test in the child process.  Exit with 0 for success,
-                * non-zero for failure
-                */
-               test_fn();
-               exit(0);
-       }
-
-       return rc;
-}
-
-/* 'str_tests' are the tests to be skipped, such as "1,3,4,.." */
-static void set_tests_skipped(char *str_tests)
-{
-       char *ptr = str_tests;
-       int tstno;
-
-       if (ptr == NULL || strlen(ptr) == 0)
-               return;
-
-       while (*ptr != '\0') {
-               tstno = strtoul(ptr, &ptr, 0);
-               if (tstno >= 0 && tstno < NUM_TESTS)
-                       test_tbl[tstno].tte_skip = true;
-               if (*ptr == ',')
-                       ptr++;
-               else
-                       break;
-       }
-}
-
-static void set_tests_to_run(char *str_tests)
-{
-       char *ptr = str_tests;
-       int tstno;
-       int i = 0;
-
-       if (ptr == NULL || strlen(ptr) == 0)
-               return;
-
-       for (i = 0; i < NUM_TESTS ; i++)
-               test_tbl[i].tte_skip = true;
-
-       while (*ptr != '\0') {
-               tstno = strtoul(ptr, &ptr, 0);
-               if (tstno >= 0 && tstno < NUM_TESTS)
-                       test_tbl[tstno].tte_skip = false;
-               if (*ptr == ',')
-                       ptr++;
-               else
-                       break;
-       }
-}
-
 static void process_args(int argc, char *argv[])
 {
        int c;
@@ -1907,20 +1756,23 @@ static void process_args(int argc, char *argv[])
        while ((c = getopt(argc, argv, "d:p:o:s:t:")) != -1) {
                switch (c) {
                case 'd':
-                       lustre_dir = optarg;
+                       if (snprintf(lustre_dir, sizeof(lustre_dir), "%s",
+                                    optarg) >= sizeof(lustre_dir))
+                               DIE("Error: test directory name too long\n");
                        break;
                case 'p':
                        poolname = optarg;
                        break;
                case 'o':
                        num_osts = atoi(optarg);
+                       if (num_osts < 2)
+                               DIE("Error: at least 2 OSTS are required\n");
                        break;
                case 's':
-                       set_tests_skipped(optarg);
+                       set_tests_to_skip(optarg, test_tbl);
                        break;
                case 't':
-                       run_list_provided = true;
-                       set_tests_to_run(optarg);
+                       set_tests_to_run(optarg, test_tbl);
                        break;
                case '?':
                        fprintf(stderr, "Unknown option '%c'\n", optopt);
@@ -1931,47 +1783,7 @@ static void process_args(int argc, char *argv[])
 
 int main(int argc, char *argv[])
 {
-       int rc = 0;
-       int i;
-       struct stat s;
-       char fsname[8 + 1];
-
-       llapi_msg_set_level(LLAPI_MSG_OFF);
-
        process_args(argc, argv);
-       if (lustre_dir == NULL)
-               lustre_dir = "/mnt/lustre";
-       if (poolname == NULL)
-               poolname = "testpool";
-       if (num_osts == -1)
-               num_osts = 2;
-
-       if (num_osts < 2)
-               DIE("Error: at least 2 OSTS are required\n");
-
-       if (stat(lustre_dir, &s) < 0)
-               DIE("cannot stat %s: %s\n", lustre_dir, strerror(errno));
-       else if (!S_ISDIR(s.st_mode))
-               DIE("%s: not a directory\n", lustre_dir);
-
-       rc = llapi_search_fsname(lustre_dir, fsname);
-       if (rc != 0) {
-               fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
-                       lustre_dir);
-               exit(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);
-
-       for (i = 0; i < NUM_TESTS; i++) {
-               struct test_tbl_entry *tst = &test_tbl[i];
-
-               if (test(tst->tte_fn, tst->tte_desc, tst->tte_skip, i) != 0)
-                       rc++;
-       }
 
-       return rc;
+       return run_tests(lustre_dir, test_tbl);
 }
diff --git a/lustre/tests/llapi_test_utils.c b/lustre/tests/llapi_test_utils.c
new file mode 100644 (file)
index 0000000..05a66f0
--- /dev/null
@@ -0,0 +1,194 @@
+// SPDX License Identifier: GPL-2.0
+/* Basic framework for Lustre llapi tests.
+ * All tests return 0 on success and non-zero on error.
+ * The program will run all tests unless a list of tests to skip is provided.
+ */
+/*
+ * Copyright 2014, 2015 Cray Inc, all rights reserved.
+ * Copyright (c) 2015, Intel Corporation.
+ * Copyright (c) 2025, DataDirect Networks, Inc. All rights reserved.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <lustre/lustreapi.h>
+#include "llapi_test_utils.h"
+
+static bool run_list_provided;
+char fsmountdir[PATH_MAX];      /* Lustre mountpoint */
+
+static void print_test_desc(int test_num, const char *test_desc,
+                           const char *status)
+{
+       int i;
+
+       i = printf(" test%u @%llu: %s ", test_num,
+                  (unsigned long long)time(NULL), test_desc);
+       for (; i < TEST_DESC_LEN; i++)
+               printf(".");
+       printf(" %s\n", status);
+}
+
+/* This function runs a single test by forking the process.  This way,
+ * if there is a segfault during a test, the test program won't crash.
+ */
+static int test(void (*test_fn)(), const char *test_desc, bool test_skip,
+               int test_num)
+{
+       int rc = 0;
+       pid_t pid;
+       char status_buf[128];
+
+       if (test_skip) {
+               if (!run_list_provided)
+                       print_test_desc(test_num, test_desc, "skip");
+               return 0;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               ERROR("cannot fork: %s", strerror(errno));
+       } else if (pid > 0) {
+               int status = 0;
+
+               /* Non-zero value indicates failure. */
+               wait(&status);
+               if (status == 0) {
+                       strncpy(status_buf, "pass", sizeof(status_buf));
+               } else if WIFSIGNALED(status) {
+                       snprintf(status_buf, sizeof(status_buf),
+                                "fail (exit status %d, killed by SIG%d)",
+                                WEXITSTATUS(status), WTERMSIG(status));
+                       rc = -1;
+               } else {
+                       snprintf(status_buf, sizeof(status_buf),
+                                "fail (exit status %d)", WEXITSTATUS(status));
+                       rc = -1;
+               }
+               print_test_desc(test_num, test_desc, status_buf);
+       } else if (pid == 0) {
+               /* Run the test in the child process.
+                * Exit 0 here for success, non-zero from test_fn() on error.
+                */
+               test_fn();
+               exit(0);
+       }
+
+       return rc;
+}
+
+/* 'str_tests' are the tests to be skipped, such as "1,3,4,..." */
+void set_tests_to_skip(const char *str_tests, struct test_tbl_entry *tst_tbl)
+{
+       const char *ptr = str_tests;
+
+       if (tst_tbl == NULL || ptr == NULL || strlen(ptr) == 0)
+               return;
+
+       while (*ptr != '\0') {
+               struct test_tbl_entry *tst;
+               char *end;
+               unsigned long tstno = strtoul(ptr, &end, 0);
+
+               if (tstno > UINT_MAX || errno)
+                       DIE("Error: invalid test number '%s'", ptr);
+
+               for (tst = tst_tbl; tst->tte_fn != NULL; tst++) {
+                       if (tst->tte_num == tstno) {
+                               tst->tte_skip = true;
+                               break;
+                       }
+               }
+               if (tst->tte_skip == false)
+                       DIE("Error: test %lu not found", tstno);
+
+               if (*end == ',')
+                       ptr = end + 1;
+               else
+                       break;
+       }
+}
+
+/* 'str_tests' are the tests to be run, such as "5,6,7,..." */
+void set_tests_to_run(const char *str_tests, struct test_tbl_entry *tst_tbl)
+{
+       struct test_tbl_entry *tst;
+       const char *ptr = str_tests;
+
+       if (tst_tbl == NULL || ptr == NULL || strlen(ptr) == 0)
+               return;
+
+       run_list_provided = true;
+       for (tst = tst_tbl; tst->tte_fn != NULL; tst++)
+               tst->tte_skip = true;
+
+       while (*ptr != '\0') {
+               struct test_tbl_entry *tst;
+               char *end;
+               unsigned long tstno = strtoul(ptr, &end, 0);
+
+               if (tstno > UINT_MAX || errno)
+                       DIE("Error: invalid test number '%s'", ptr);
+
+               for (tst = tst_tbl; tst->tte_fn != NULL; tst++) {
+                       if (tst->tte_num == tstno) {
+                               tst->tte_skip = false;
+                               break;
+                       }
+               }
+               if (tst->tte_skip == true)
+                       DIE("Error: test %lu not found", tstno);
+               if (*end == ',')
+                       ptr = end + 1;
+               else
+                       break;
+       }
+}
+
+int run_tests(const char *lustre_dir, struct test_tbl_entry *tst_tbl)
+{
+       struct test_tbl_entry *tst;
+       char fsname[8 + 1];
+       struct stat st;
+       int rc;
+
+       if (lustre_dir == NULL)
+               DIE("no test directory provided\n");
+
+       if (tst_tbl == NULL)
+               DIE("no test table provided\n");
+
+       llapi_msg_set_level(LLAPI_MSG_OFF);
+
+       if (stat(lustre_dir, &st) < 0)
+               DIE("cannot stat %s: %s\n", lustre_dir, strerror(errno));
+       else if (!S_ISDIR(st.st_mode))
+               DIE("%s: not a directory\n", lustre_dir);
+
+       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);
+
+       for (tst = tst_tbl; tst->tte_fn != NULL; tst++) {
+               if (test(tst->tte_fn, tst->tte_desc, tst->tte_skip,
+                        tst->tte_num))
+                       rc++;
+       }
+
+       return rc;
+}
diff --git a/lustre/tests/llapi_test_utils.h b/lustre/tests/llapi_test_utils.h
new file mode 100644 (file)
index 0000000..b5bcdd9
--- /dev/null
@@ -0,0 +1,48 @@
+/* SPDX License Identifier: GPL-2.0 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/signal.h>
+#include <sys/wait.h>
+
+#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 TEST_DESC_LEN  80
+struct test_tbl_entry {
+       void            (*tte_fn)(void);
+       char            tte_desc[TEST_DESC_LEN];
+       unsigned int    tte_num;
+       bool            tte_skip;
+};
+
+#define TEST_REGISTER(num) \
+       { .tte_fn = &test ## num, .tte_desc = T ## num ## _DESC, .tte_num = num}
+#define TEST_REGISTER_END { .tte_fn = NULL }
+
+extern char fsmountdir[PATH_MAX];      /* Lustre mountpoint */
+
+/* Run all tests declared in @tst_tbl until a NULL entry is found.
+ * The tests are run by forking the process.  That way, if test segfaults,
+ * the test program won't crash.
+ * Tests may be skipped by setting .tte_skip = true.
+ */
+int run_tests(const char *lustre_dir, struct test_tbl_entry *tst_tbl);
+
+/* 'str_tests' are the tests to be skipped/run, such as "1,3,4,.." */
+void set_tests_to_skip(const char *str_tests, struct test_tbl_entry *tst_tbl);
+void set_tests_to_run(const char *str_tests, struct test_tbl_entry *tst_tbl);
index cfc5cf9..1a7002a 100755 (executable)
@@ -5800,20 +5800,22 @@ run_test 411 "hsm_ops rbac role"
 
 test_500()
 {
-       [ "$MDS1_VERSION" -lt $(version_code 2.6.92) ] &&
-               skip "HSM migrate is not supported"
+       local bitmap_opt=""
+
+       (( $MDS1_VERSION >= $(version_code 2.6.92-47-g1fe3ae8dab) )) ||
+               skip "need MDS >= 2.6.92.47 for HSM migrate support"
 
        test_mkdir -p $DIR/$tdir
 
-       if [ "$CLIENT_VERSION" -lt $(version_code 2.11.56) ] ||
-            [ "$MDS1_VERSION" -lt $(version_code 2.11.56) ];
-       then
-               llapi_hsm_test -d $DIR/$tdir -b ||
-                       error "One llapi HSM test failed"
-       else
-               llapi_hsm_test -d $DIR/$tdir ||
-                       error "One llapi HSM test failed"
-       fi
+       (( $CLIENT_VERSION >= $(version_code 2.11.56-179-g3bfb6107ba) &&
+          $MDS1_VERSION >= $(version_code 2.11.56-179-g3bfb6107ba) )) ||
+               bitmap_opt="-b"
+
+       (( $MDS1_VERSION >= $(version_code 2.14.50-142-gf684172237) )) ||
+               SKIP500+=" -s 113"
+
+       llapi_hsm_test -d $DIR/$tdir $bitmap_opt $SKIP500 ||
+               error "llapi HSM testing failed"
 }
 run_test 500 "various LLAPI HSM tests"
 
index 242d43e..ee16f20 100755 (executable)
@@ -2575,7 +2575,7 @@ test_27Cj() {
 run_test 27Cj "overstriping with -C for max values in multiple of targets"
 
 test_27D() {
-       [ $OSTCOUNT -lt 2 ] && skip_env "needs >= 2 OSTs"
+       (( $OSTCOUNT >= 2 )) || skip_env "needs >= 2 OSTs"
        remote_mds_nodsh && skip "remote MDS with nodsh"
 
        local POOL=${POOL:-testpool}
@@ -2589,18 +2589,17 @@ test_27D() {
        pool_add $POOL || error "pool_add failed"
        pool_add_targets $POOL $ost_range || error "pool_add_targets failed"
 
-       local skip27D
-       [ $MDS1_VERSION -lt $(version_code 2.8.55) ] &&
-               skip27D+="-s 29"
-       [ $MDS1_VERSION -lt $(version_code 2.9.55) ] ||
-               [ $CLIENT_VERSION -lt $(version_code 2.9.55) ] &&
-                       skip27D+=" -s 30,31"
-       [[ ! $($LCTL get_param mdc.*.import) =~ connect_flags.*overstriping ||
-         $OSTCOUNT -ge $(($LOV_MAX_STRIPE_COUNT / 2)) ]] &&
-               skip27D+=" -s 32,33"
-       [[ $MDS_VERSION -lt $(version_code $SEL_VER) ]] &&
-               skip27D+=" -s 34"
-       llapi_layout_test -d$DIR/$tdir -p$POOL -o$OSTCOUNT $skip27D ||
+       (( $MDS1_VERSION >= $(version_code 2.8.55) )) ||
+               SKIP27D+=" -s 29"
+       (( $MDS1_VERSION >= $(version_code 2.9.55) &&
+          $CLIENT_VERSION >= $(version_code 2.9.55) )) ||
+               SKIP27D+=" -s 30,31"
+       [[ $($LCTL get_param mdc.*.import) =~ connect_flags.*overstriping ]] &&
+       (( $OSTCOUNT < $LOV_MAX_STRIPE_COUNT / 2)) ||
+               SKIP27D+=" -s 32,33"
+       (( $MDS1_VERSION >= $(version_code $SEL_VER) )) ||
+               SKIP27D+=" -s 34"
+       llapi_layout_test -d$DIR/$tdir -p$POOL -o$OSTCOUNT $SKIP27D ||
                error "llapi_layout_test failed"
 
        destroy_test_pools || error "destroy test pools failed"