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)
-/*
- * 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;
"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;
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;
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;
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;
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;
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. */
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;
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;
close(fd);
}
-/* Test llapi_hsm_current_action */
+#define T52_DESC "Test llapi_hsm_current_action()"
static void test52(void)
{
int rc;
"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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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:
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);
}
-/*
- * 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] "
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;
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);
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);
}
--- /dev/null
+// 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;
+}
--- /dev/null
+/* 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);
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"
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}
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"