4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License version 2 for more details. A copy is
14 * included in the COPYING file that accompanied this code.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * Copyright (c) 2017, Intel Corporation.
25 * lustre/utils/lsnapshot.c
27 * Author: Fan, Yong <fan.yong@intel.com>
37 #include <sys/types.h>
45 #include <libcfs/util/list.h>
46 #include <libcfs/util/ioctl.h>
47 #include <linux/lustre/lustre_ioctl.h>
48 #include <linux/lustre/lustre_barrier_user.h>
52 #define SNAPSHOT_CONF_DIR "/etc/lsnapshot"
53 #define LDEV_CONF "/etc/ldev.conf"
54 #define SNAPSHOT_LOG "/var/log/lsnapshot.log"
55 #define SNAPSHOT_MAGIC "0x14F711B9"
56 #define MAX_BUF_SIZE 4096
64 struct snapshot_target {
65 struct list_head st_list;
66 /* Target node name. */
68 /* Where the pool is */
70 /* The target pool name on the target node. */
72 /* The backend filesystem name against the target pool. */
75 unsigned int st_index;
83 struct snapshot_instance {
84 struct list_head si_mdts_list;
85 struct list_head si_osts_list;
86 struct snapshot_target *si_mgs;
87 struct snapshot_target *si_mdt0;
101 static const char snapshot_rsh_default[] = "ssh";
103 static char *snapshot_role2name(char *name, enum snapshot_role role,
107 snprintf(name, 8, "MDT%04x", index);
108 else if (role & SR_MGS)
109 snprintf(name, 4, "MGS");
111 snprintf(name, 8, "OST%04x", index);
116 #define SNAPSHOT_ADD_LOG(si, format, ...) \
118 char buf[MAX_BUF_SIZE]; \
122 memset(buf, 0, sizeof(buf)); \
124 snprintf(buf, sizeof(buf) - 1, "%s", ctime(&tt)); \
125 ptr = strrchr(buf, '\n'); \
129 fprintf(si->si_log_fp, "%s (%d:%s:%d:%s:%s): "format, buf, \
130 getpid(), __func__, __LINE__, si->si_fsname, \
131 si->si_rsh, ## __VA_ARGS__); \
134 char *snapshot_fgets(FILE *fp, char *buf, int buflen)
138 memset(buf, 0, buflen);
139 if (fgets(buf, buflen, fp) == NULL)
142 ptr = strchr(buf, '\n');
149 static int snapshot_exec(const char *cmd)
155 /* system() return value depends on both the system() general framework,
156 * such as whether fork()/exec() success or fail, and the real @cmd exec
157 * result. Especially, if the @cmd is remote command, we may cannot know
158 * the real failure. */
160 /* fork()/exec() error */
162 return errno != 0 ? -errno : -1;
165 rc = WEXITSTATUS(rc);
168 } else if (WIFSIGNALED(rc)) {
171 /* all other known or unknown cases. */
178 static int snapshot_load_conf_ldev(struct snapshot_instance *si, char *buf,
179 struct snapshot_target *st, char **role)
189 rc = sscanf(buf, "%ms %ms %ms %ms",
190 &st->st_host, &ignore, &label, &device);
199 * [md|zfs:][pool_dir/]<pool>/<filesystem> */
200 ptr = strchr(device, ':');
203 if (strncmp(device, "zfs:", strlen("zfs:")) != 0) {
211 ptr1 = strrchr(ptr, '/');
212 /* "ptr1 - ptr + 1 == strlen(ptr)" means '/' is at the tail. */
213 if (!ptr1 || ptr1 == ptr || ptr1 - ptr + 1 == strlen(ptr)) {
219 st->st_filesystem = malloc(len);
220 if (!st->st_filesystem) {
226 strncpy(st->st_filesystem, ptr1 + 1, len - 1);
227 st->st_filesystem[len - 1] = '\0';
230 ptr1 = strrchr(ptr, '/');
233 st->st_dir = malloc(len + 1);
239 strncpy(st->st_dir, ptr, len);
240 st->st_dir[len] = '\0';
245 st->st_pool = malloc(len + 1);
251 strncpy(st->st_pool, ptr, len);
252 st->st_pool[len] = '\0';
255 * fsname-<role><index> or <role><index> */
256 ptr = strchr(label, '-');
258 if (strncmp(si->si_fsname, label, ptr - label) != 0) {
259 /* This line is NOT for current filesystem .*/
269 if (strlen(ptr) < 3 || strlen(ptr) > 7) {
280 strncpy(*role, ptr, 3);
284 while (isxdigit(ptr[len])) {
285 if (isdigit(ptr[len]))
287 st->st_index * 16 + ptr[len] - '0';
288 else if (isupper(ptr[len]))
290 st->st_index * 16 + ptr[len] - 'A' + 10;
293 st->st_index * 16 + ptr[len] - 'a' + 10;
298 if (strncasecmp(*role, "MGS", 3) != 0)
306 if (!isxdigit(ptr[len]) && ptr[len] != '\0') {
321 * For old snasphot tools, the configration is in /etc/lsnapshot/${fsname}.conf,
323 * <host> <pool_dir> <pool> <local_fsname> <role(,s)> <index>
327 * host-mdt1 /tmp myfs-mdt1 mdt1 MGS,MDT 0
328 * host-mdt2 /tmp myfs-mdt2 mdt2 MDT 1
329 * host-ost1 /tmp myfs-ost1 ost1 OST 0
330 * host-ost2 /tmp myfs-ost2 ost2 OST 1
333 * For new snasphot tools, the configration is in /etc/ldev.conf, which is not
334 * only for snapshot, but also for other purpose. The format is:
335 * <host> foreign/- <label> <device> [journal-path]/- [raidtab]
337 * The format of <label> is:
338 * fsname-<role><index> or <role><index>
340 * The format of <device> is:
341 * [md|zfs:][pool_dir/]<pool>/<filesystem>
343 * Snapshot only uses the fields <host>, <label> and <device>.
347 * host-mdt1 - myfs-MDT0000 zfs:/tmp/myfs-mdt1/mdt1
350 * \retval 0 for success
351 * \retval +ve the line# with which the current line is conflict
352 * \retval -EAGAIN skip current line
353 * \retval -ve other failures
355 static int snapshot_load_conf_one(struct snapshot_instance *si,
356 char *buf, int line_num, bool is_ldev)
358 struct snapshot_target *st;
362 /* filter out space */
363 while (isspace(*buf))
366 /* skip empty line */
370 /* skip comment line */
374 st = malloc(sizeof(*st));
378 memset(st, 0, sizeof(*st));
379 INIT_LIST_HEAD(&st->st_list);
382 rc = snapshot_load_conf_ldev(si, buf, st, &role);
384 rc = sscanf(buf, "%ms %ms %ms %ms %ms %d",
385 &st->st_host, &st->st_dir, &st->st_pool,
386 &st->st_filesystem, &role, &st->st_index);
395 if (strncasecmp(role, "MGS", 3) == 0) {
396 st->st_role = SR_MGS;
397 if (role[3] == ',') {
399 if (strncasecmp(&role[4], "MDT", 3) != 0) {
404 st->st_role |= SR_MDT;
406 } else if (strncasecmp(role, "MDT", 3) == 0) {
407 st->st_role = SR_MDT;
408 if (role[3] == ',') {
410 if (strncasecmp(&role[4], "MGS", 3) != 0) {
415 st->st_role |= SR_MGS;
417 } else if (strncasecmp(role, "OST", 3) == 0) {
418 st->st_role = SR_OST;
424 st->st_line = line_num;
425 if (st->st_role & SR_MDT) {
426 /* MGS is the first, MDT0 is just after the MGS
427 * if they are not combined together. */
428 if (st->st_role & SR_MGS) {
430 rc = si->si_mgs->st_line;
435 list_add(&st->st_list, &si->si_mdts_list);
438 if (st->st_index == 0) {
440 rc = si->si_mdt0->st_line;
445 if (list_empty(&st->st_list)) {
446 if (list_empty(&si->si_mdts_list) ||
448 list_add(&st->st_list,
451 list_add(&st->st_list,
452 &si->si_mgs->st_list);
454 } else if (list_empty(&st->st_list)) {
455 list_add_tail(&st->st_list, &si->si_mdts_list);
457 } else if (st->st_role & SR_MGS) {
459 rc = si->si_mgs->st_line;
464 list_add(&st->st_list, &si->si_mdts_list);
466 list_add_tail(&st->st_list, &si->si_osts_list);
480 if (st->st_filesystem)
481 free(st->st_filesystem);
488 static int snapshot_load_conf(struct snapshot_instance *si, int lock_mode)
491 char buf[MAX_BUF_SIZE];
498 memset(conf_name, 0, sizeof(conf_name));
499 strncpy(conf_name, LDEV_CONF, sizeof(conf_name) - 1);
500 fd = open(conf_name, O_RDONLY);
502 if (errno != ENOENT) {
504 "Can't open the snapshot config file %s: %s\n",
505 conf_name, strerror(errno));
510 snprintf(conf_name, sizeof(conf_name) - 1, "%s/%s.conf",
511 SNAPSHOT_CONF_DIR, si->si_fsname);
512 fd = open(conf_name, O_RDONLY);
515 "Can't open the snapshot config file %s: %s\n",
516 conf_name, strerror(errno));
524 rc = flock(fd, lock_mode | LOCK_NB);
527 "Can't lock the snapshot config file %s (%d): %s\n",
528 conf_name, lock_mode, strerror(errno));
533 fp = fdopen(fd, "r");
536 "Can't fdopen the snapshot config file %s: %s\n",
537 conf_name, strerror(errno));
542 while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
543 rc = snapshot_load_conf_one(si, buf, line_num, is_ldev);
546 "Invalid snapshot config file %s at the line "
547 "%d '%s'\n", conf_name, line_num, buf);
548 } else if (rc == -EAGAIN) {
552 "The config role has been specified repeatedly "
553 "at the lines %d/%d in %s\n",
554 rc, line_num, conf_name);
566 "Miss MDT0 in the config file %s\n",
572 /* By default, the MGS is on the MDT0 if it is not specified. */
574 si->si_mgs = si->si_mdt0;
575 si->si_mgs->st_role |= SR_MGS;
578 if (list_empty(&si->si_osts_list)) {
580 "Miss OST(s) in the config file %s\n",
599 static void snapshot_unload_conf(struct snapshot_instance *si)
601 struct snapshot_target *st;
603 while (!list_empty(&si->si_mdts_list)) {
604 st = list_entry(si->si_mdts_list.next,
605 struct snapshot_target, st_list);
606 list_del(&st->st_list);
610 free(st->st_filesystem);
614 while (!list_empty(&si->si_osts_list)) {
615 st = list_entry(si->si_osts_list.next,
616 struct snapshot_target, st_list);
617 list_del(&st->st_list);
621 free(st->st_filesystem);
628 if (si->si_conf_fd >= 0) {
629 flock(si->si_conf_fd, LOCK_UN);
630 close(si->si_conf_fd);
635 static int snapshot_handle_string_option(char **dst, const char *option,
636 const char *opt_name)
640 if (*dst && *dst != snapshot_rsh_default) {
641 fprintf(stderr, "specify the %s repeatedly.\n", opt_name);
645 len = strlen(option);
646 *dst = malloc(len + 1);
650 strncpy(*dst, option, len);
655 static void snapshot_fini(struct snapshot_instance *si)
657 snapshot_unload_conf(si);
660 fclose(si->si_log_fp);
662 if (si->si_rsh && si->si_rsh != snapshot_rsh_default)
668 if (si->si_new_ssname)
669 free(si->si_new_ssname);
671 free(si->si_comment);
676 static struct snapshot_instance *
677 snapshot_init(int argc, char * const argv[], const struct option *longopts,
678 const char *optstring, void (*usage)(void),
679 int lock_mode, int *err)
681 struct snapshot_instance *si;
686 si = malloc(sizeof(*si));
689 "No enough memory to initialize snapshot instance.\n");
694 memset(si, 0, sizeof(*si));
695 INIT_LIST_HEAD(&si->si_mdts_list);
696 INIT_LIST_HEAD(&si->si_osts_list);
697 si->si_rsh = (char *)snapshot_rsh_default;
699 si->si_timeout = BARRIER_TIMEOUT_DEFAULT;
700 si->si_barrier = true;
701 si->si_detail = false;
702 si->si_force = false;
704 while ((opt = getopt_long(argc, argv, optstring,
705 longopts, &idx)) != EOF) {
708 if (!optarg || strcmp(optarg, "on") == 0) {
709 si->si_barrier = true;
710 } else if (strcmp(optarg, "off") == 0) {
711 si->si_barrier = false;
719 *err = snapshot_handle_string_option(&si->si_comment,
725 si->si_detail = true;
731 *err = snapshot_handle_string_option(&si->si_fsname,
737 *err = snapshot_handle_string_option(&si->si_ssname,
743 *err = snapshot_handle_string_option(&si->si_new_ssname,
750 *err = snapshot_handle_string_option(&si->si_rsh,
757 si->si_timeout = atoi(optarg);
771 if (!si->si_fsname) {
772 fprintf(stderr, "The fsname must be specified\n");
778 if (strlen(si->si_fsname) > 8) {
779 fprintf(stderr, "Invalid fsname %s\n", si->si_fsname);
784 si->si_log_fp = fopen(SNAPSHOT_LOG, "a");
785 if (!si->si_log_fp) {
788 "Can't open the snapshot log file %s: %s\n",
789 SNAPSHOT_LOG, strerror(errno));
793 *err = snapshot_load_conf(si, lock_mode);
796 if (*err != 0 && si) {
804 static int __snapshot_wait(struct snapshot_instance *si,
805 struct list_head *head, int *err)
807 struct snapshot_target *st;
811 list_for_each_entry(st, head, st_list) {
815 rc = waitpid(st->st_pid, &st->st_status, 0);
817 SNAPSHOT_ADD_LOG(si, "Can't wait child (%d) operation "
818 "on the target <%s:%x:%d>: %s\n",
819 st->st_pid, st->st_host, st->st_role,
820 st->st_index, strerror(errno));
826 /* continue to wait for next */
830 if (WIFEXITED(st->st_status)) {
831 rc = WEXITSTATUS(st->st_status);
836 st->st_ignored = true;
842 } else if (WIFSIGNALED(st->st_status)) {
843 SNAPSHOT_ADD_LOG(si, "The child (%d) operation on the "
844 "target <%s:%x:%d> was killed by "
846 st->st_pid, st->st_host, st->st_role,
847 st->st_index, WTERMSIG(st->st_status));
852 SNAPSHOT_ADD_LOG(si, "The child (%d) operation on the "
853 "target <%s:%x:%d> failed for "
855 st->st_pid, st->st_host, st->st_role,
868 static int snapshot_wait(struct snapshot_instance *si, int *err)
872 count = __snapshot_wait(si, &si->si_mdts_list, err);
873 count += __snapshot_wait(si, &si->si_osts_list, err);
878 static char *snapshot_first_skip_blank(char *buf)
882 ptr = strchr(buf, ' ');
884 ptr = strchr(buf, '\t');
889 while (*ptr == ' ' || *ptr == '\t')
898 static int mdt0_is_lustre_snapshot(struct snapshot_instance *si)
900 char buf[MAX_BUF_SIZE];
904 memset(buf, 0, sizeof(buf));
905 snprintf(buf, sizeof(buf) - 1,
906 "%s %s 'zpool import -d %s %s > /dev/null 2>&1; "
907 "zfs get -H -o value lustre:magic %s/%s@%s'",
908 si->si_rsh, si->si_mdt0->st_host,
909 si->si_mdt0->st_dir ? si->si_mdt0->st_dir :
911 si->si_mdt0->st_pool, si->si_mdt0->st_pool,
912 si->si_mdt0->st_filesystem, si->si_ssname);
913 fp = popen(buf, "r");
915 SNAPSHOT_ADD_LOG(si, "Popen fail to check snapshot "
916 "on mdt0: %s\n", strerror(errno));
920 if (snapshot_fgets(fp, buf, strlen(SNAPSHOT_MAGIC) + 1) == NULL) {
922 } else if (strcmp(buf, SNAPSHOT_MAGIC) == 0) {
926 "The target %s is not Lustre snapshot "
927 "or it does not exists\n", si->si_ssname);
935 static int target_is_mounted(struct snapshot_instance *si,
936 struct snapshot_target *st, const char *ssname)
938 char buf[MAX_BUF_SIZE];
939 char fullname[MAX_BUF_SIZE];
944 memset(buf, 0, sizeof(buf));
945 snprintf(buf, sizeof(buf) - 1, "%s %s 'mount'",
946 si->si_rsh, st->st_host);
947 fp = popen(buf, "r");
949 SNAPSHOT_ADD_LOG(si, "Popen fail to check target mount: %s\n",
954 memset(fullname, 0, sizeof(fullname));
956 snprintf(fullname, sizeof(fullname) - 1, "%s/%s@%s on ",
957 st->st_pool, st->st_filesystem, ssname);
959 snprintf(fullname, sizeof(fullname) - 1, "%s/%s on ",
960 st->st_pool, st->st_filesystem);
962 while (snapshot_fgets(fp, buf, sizeof(buf)) != NULL) {
963 ptr = strstr(buf, fullname);
967 ptr += strlen(fullname) + 1; /* mount point */
968 if (ptr >= buf + strlen(buf))
971 ptr = strstr(ptr, "type lustre");
982 static int snapshot_get_fsname(struct snapshot_instance *si,
983 char *fsname, int fslen)
985 char buf[MAX_BUF_SIZE];
989 memset(buf, 0, sizeof(buf));
990 snprintf(buf, sizeof(buf) - 1,
991 "%s %s 'zpool import -d %s %s > /dev/null 2>&1; "
992 "zfs get -H -o value lustre:fsname %s/%s@%s'",
993 si->si_rsh, si->si_mdt0->st_host,
994 si->si_mdt0->st_dir ? si->si_mdt0->st_dir :
996 si->si_mdt0->st_pool, si->si_mdt0->st_pool,
997 si->si_mdt0->st_filesystem, si->si_ssname);
998 fp = popen(buf, "r");
1000 SNAPSHOT_ADD_LOG(si, "Popen fail to get fsname: %s\n",
1005 if (snapshot_fgets(fp, fsname, fslen) == NULL)
1012 static int snapshot_get_mgsnode(struct snapshot_instance *si,
1013 char *node, int size)
1015 char buf[MAX_BUF_SIZE];
1016 struct snapshot_target *st;
1020 st = list_entry(si->si_osts_list.next, struct snapshot_target,
1022 memset(buf, 0, sizeof(buf));
1023 snprintf(buf, sizeof(buf) - 1,
1024 "%s %s 'zfs get -H -o value lustre:mgsnode %s/%s'",
1025 si->si_rsh, st->st_host, st->st_pool, st->st_filesystem);
1026 fp = popen(buf, "r");
1028 SNAPSHOT_ADD_LOG(si, "Popen fail to get mgsnode: %s\n",
1033 if (snapshot_fgets(fp, node, size) == NULL)
1040 static int snapshot_exists_check(struct snapshot_instance *si,
1041 struct snapshot_target *st)
1043 char buf[MAX_BUF_SIZE];
1047 memset(buf, 0, sizeof(buf));
1048 snprintf(buf, sizeof(buf) - 1,
1049 "%s %s 'zfs list %s/%s@%s 2>/dev/null'",
1050 si->si_rsh, st->st_host, st->st_pool,
1051 st->st_filesystem, si->si_ssname);
1052 fp = popen(buf, "r");
1054 SNAPSHOT_ADD_LOG(si, "Popen fail to create check: %s\n",
1059 if (snapshot_fgets(fp, buf, sizeof(buf)) != NULL)
1066 static int snapshot_general_check(struct snapshot_instance *si)
1068 return mdt0_is_lustre_snapshot(si);
1071 static void snapshot_create_usage(void)
1074 "Create snapshot for the given filesystem.\n"
1076 "snapshot_create [-b | --barrier [on | off]] "
1077 "[-c | --comment comment] "
1078 "<-F | --fsname fsname> "
1079 "[-h | --help] <-n | --name ssname> "
1080 "[-r | --rsh remote_shell]"
1081 "[-t | --timeout timeout]\n"
1083 "-b: set write barrier before creating snapshot, "
1084 "the default value is 'on'.\n"
1085 "-c: describe what the snapshot is for, and so on.\n"
1086 "-F: the filesystem name.\n"
1087 "-h: for help information.\n"
1088 "-n: the snapshot's name.\n"
1089 "-r: the remote shell used for communication with remote "
1090 "target, the default value is 'ssh'.\n"
1091 "-t: the life cycle (seconds) for write barrier, "
1092 "the default value is %d seconds.\n",
1093 BARRIER_TIMEOUT_DEFAULT);
1096 static int snapshot_create_check(struct snapshot_instance *si)
1100 rc = snapshot_exists_check(si, si->si_mdt0);
1102 fprintf(stderr, "The snapshot %s exists\n", si->si_ssname);
1107 static int snapshot_inherit_prop(struct snapshot_instance *si,
1108 struct snapshot_target *st,
1109 char *cmd, int size)
1111 char buf[MAX_BUF_SIZE];
1116 memset(buf, 0, sizeof(buf));
1117 snprintf(buf, sizeof(buf) - 1,
1118 "%s %s \"zpool import -d %s %s > /dev/null 2>&1; "
1119 "zfs get all %s/%s | grep lustre: | grep local$ | "
1120 "awk '{ \\$1=\\\"\\\"; \\$NF=\\\"\\\"; print \\$0 }' | "
1121 "sed -e 's/^ //'\"",
1122 si->si_rsh, st->st_host,
1123 st->st_dir ? st->st_dir : "/dev -d /tmp",
1124 st->st_pool, st->st_pool, st->st_filesystem);
1125 fp = popen(buf, "r");
1127 SNAPSHOT_ADD_LOG(si, "Popen fail to list one: %s\n",
1132 while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
1136 if (strncmp(buf, "lustre:fsname",
1137 strlen("lustre:fsname")) == 0)
1140 if (strncmp(buf, "lustre:magic",
1141 strlen("lustre:magic")) == 0)
1144 if (strncmp(buf, "lustre:ctime",
1145 strlen("lustre:ctime")) == 0)
1148 if (strncmp(buf, "lustre:mtime",
1149 strlen("lustre:mtime")) == 0)
1152 if (strncmp(buf, "lustre:comment",
1153 strlen("lustre:comment")) == 0)
1156 if (strncmp(buf, "lustre:svname",
1157 strlen("lustre:svname")) == 0)
1160 if (strncmp(buf, "lustre:mgsnode",
1161 strlen("lustre:mgsnode")) == 0)
1164 ptr = strchr(buf, ' ');
1166 ptr = strchr(buf, '\t');
1173 while (*ptr == ' ' || *ptr == '\t')
1179 end = strchr(ptr, ' ');
1181 end = strchr(buf, '\t');
1185 rc = snprintf(cmd + len, size - len - 1,
1186 "-o %s=\"%s\" ", buf, ptr);
1197 static int __snapshot_create(struct snapshot_instance *si,
1198 struct list_head *head, const char *fsname,
1199 const char *mgsnode, __u64 xtime)
1201 struct snapshot_target *st;
1205 list_for_each_entry(st, head, st_list) {
1212 SNAPSHOT_ADD_LOG(si, "Can't fork for create snapshot "
1213 "(%s@%s <%s>) on the target "
1215 fsname, si->si_ssname,
1216 si->si_comment, st->st_host,
1217 st->st_role, st->st_index,
1224 char cmd[MAX_BUF_SIZE];
1227 memset(cmd, 0, sizeof(cmd));
1228 len = snprintf(cmd, sizeof(cmd) - 1,
1229 "%s %s 'zfs snapshot "
1230 "-o lustre:fsname=%s "
1231 "-o lustre:magic=%s "
1232 "-o lustre:ctime=%llu "
1233 "-o lustre:mtime=%llu ",
1234 si->si_rsh, st->st_host, fsname,
1235 SNAPSHOT_MAGIC, xtime, xtime);
1239 if (si->si_comment) {
1240 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1241 "-o lustre:comment=\"%s\" ",
1249 /* Make the inherited properties as local ones,
1250 * then even if others changed (or removed) the
1251 * property of the parent dataset, the snapshot
1252 * will not be affected. */
1253 rc = snapshot_inherit_prop(si, st, cmd + len,
1254 MAX_BUF_SIZE - len - 1);
1256 SNAPSHOT_ADD_LOG(si, "Can't filter property on "
1257 "target (%s:%x:%d): rc = %d\n",
1258 st->st_host, st->st_role,
1265 if (st->st_role & SR_OST)
1266 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1267 "-o lustre:svname=%s-OST%04x "
1268 "-o lustre:mgsnode=%s %s/%s@%s'",
1269 fsname, st->st_index, mgsnode,
1270 st->st_pool, st->st_filesystem,
1272 else if (!(st->st_role & SR_MGS) ||
1273 /* MGS is on MDT0 */
1274 si->si_mdt0 == si->si_mgs)
1275 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1276 "-o lustre:svname=%s-MDT%04x "
1277 "-o lustre:mgsnode=%s %s/%s@%s'",
1278 fsname, st->st_index, mgsnode,
1279 st->st_pool, st->st_filesystem,
1283 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1284 "%s/%s@%s'", st->st_pool,
1285 st->st_filesystem, si->si_ssname);
1289 rc = snapshot_exec(cmd);
1291 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on "
1292 "target (%s:%x:%d): rc = %d\n",
1293 cmd, st->st_host, st->st_role,
1297 } /* end of child */
1299 /* parent continue to run more snapshot commands in parallel. */
1306 static int __snapshot_destroy(struct snapshot_instance *si,
1307 struct list_head *head);
1309 static int snapshot_create(struct snapshot_instance *si)
1312 char buf[MAX_BUF_SIZE];
1319 rc = snapshot_create_check(si);
1323 rc = gettimeofday(&tv, NULL);
1327 srandom(tv.tv_usec);
1328 snprintf(new_fsname, 8, "%08x", (__u32)random());
1329 new_fsname[8] = '\0';
1331 rc = snapshot_get_mgsnode(si, buf, sizeof(buf));
1335 __argv[1] = si->si_fsname;
1336 /* 1. Get barrier */
1337 if (si->si_barrier) {
1340 memset(tbuf, 0, sizeof(tbuf));
1341 snprintf(tbuf, 7, "%u", si->si_timeout);
1342 __argv[0] = "barrier_freeze";
1344 rc = jt_barrier_freeze(3, __argv);
1346 SNAPSHOT_ADD_LOG(si, "Can't set barrier within %u "
1347 "seconds on %s: rc = %d\n",
1348 si->si_timeout, si->si_fsname, rc);
1354 /* 2. Fork config llog on MGS */
1355 __argv[0] = "fork_lcfg";
1356 __argv[2] = new_fsname;
1357 rc = jt_lcfg_fork(3, __argv);
1359 SNAPSHOT_ADD_LOG(si, "Can't fork config log for create "
1360 "snapshot %s from %s to %s: rc = %d\n",
1361 si->si_ssname, si->si_fsname, new_fsname, rc);
1365 /* 3.1 Create snapshot on every MDT */
1366 rc = __snapshot_create(si, &si->si_mdts_list, new_fsname, buf,
1369 /* 3.2 Create snapshot on every OST */
1370 rc = __snapshot_create(si, &si->si_osts_list, new_fsname, buf,
1373 /* 4. Wait for all children, even though part of them maybe failed */
1374 snapshot_wait(si, &rc1);
1377 /* 5. Put barrier */
1378 if (si->si_barrier) {
1380 struct barrier_ctl bc;
1382 rc = __jt_barrier_stat(__argv[1], &bc);
1384 SNAPSHOT_ADD_LOG(si, "Can't get barrier status "
1387 } else if (bc.bc_status != BS_FROZEN ||
1388 bc.bc_timeout <= 0) {
1389 SNAPSHOT_ADD_LOG(si, "The barrier expired "
1390 "on %s\n", si->si_fsname);
1395 __argv[0] = "barrier_thaw";
1396 rc2 = jt_barrier_thaw(2, __argv);
1398 SNAPSHOT_ADD_LOG(si, "Can't release barrier on %s: "
1399 "rc = %d\n", si->si_fsname, rc2);
1404 si->si_force = true;
1405 __snapshot_destroy(si, &si->si_osts_list);
1406 __snapshot_destroy(si, &si->si_mdts_list);
1407 snapshot_wait(si, &rc2);
1409 __argv[0] = "erase_lcfg";
1410 __argv[1] = new_fsname;
1412 jt_lcfg_erase(3, __argv);
1415 return rc ? rc : (rc1 ? rc1 : rc2);
1418 int jt_snapshot_create(int argc, char **argv)
1420 struct snapshot_instance *si;
1421 struct option long_opts[] = {
1422 { .val = 'b', .name = "barrier", .has_arg = optional_argument },
1423 { .val = 'c', .name = "comment", .has_arg = required_argument },
1424 { .val = 'F', .name = "fsname", .has_arg = required_argument },
1425 { .val = 'h', .name = "help", .has_arg = no_argument },
1426 { .val = 'n', .name = "name", .has_arg = required_argument },
1427 { .val = 'r', .name = "rsh", .has_arg = required_argument },
1428 { .val = 't', .name = "timeout", .has_arg = required_argument },
1432 si = snapshot_init(argc, argv, long_opts, "b::c:F:hn:r:t:",
1433 snapshot_create_usage, LOCK_EX, &rc);
1437 if (!si->si_ssname) {
1439 "Miss the snapshot name to be created\n");
1440 snapshot_create_usage();
1445 rc = snapshot_create(si);
1448 "Can't create the snapshot %s\n", si->si_ssname);
1449 SNAPSHOT_ADD_LOG(si, "Can't create snapshot %s with "
1450 "comment <%s> barrier <%s>, timeout "
1452 si->si_ssname, si->si_comment,
1453 si->si_barrier ? "enable" : "disable",
1454 si->si_barrier ? si->si_timeout : -1, rc);
1456 SNAPSHOT_ADD_LOG(si, "Create snapshot %s successfully "
1457 "with comment <%s>, barrier <%s>, "
1459 si->si_ssname, si->si_comment,
1460 si->si_barrier ? "enable" : "disable",
1461 si->si_barrier ? si->si_timeout : -1);
1468 static void snapshot_destroy_usage(void)
1471 "Destroy the specified snapshot.\n"
1473 "snapshot_destroy [-f | --force] "
1474 "<-F | --fsname fsname> [-h | --help] "
1475 "<-n | --name ssname> "
1476 "[-r | --rsh remote_shell]\n"
1478 "-f: destroy the snapshot by force.\n"
1479 "-F: the filesystem name.\n"
1480 "-h: for help information.\n"
1481 "-n: the snapshot's name.\n"
1482 "-r: the remote shell used for communication with remote "
1483 "target, the default value is 'ssh'.\n");
1486 static int snapshot_destroy_check(struct snapshot_instance *si)
1488 struct list_head *head = &si->si_osts_list;
1489 struct snapshot_target *st;
1494 list_for_each_entry(st, head, st_list) {
1501 SNAPSHOT_ADD_LOG(si, "Can't fork for check snapshot "
1502 "%s on the target (%s:%x:%d): %s\n",
1503 si->si_ssname, st->st_host,
1504 st->st_role, st->st_index,
1511 rc = snapshot_exists_check(si, st);
1513 /* The snapshot piece does not exist */
1516 exit(rc == -EEXIST ? 0: rc);
1517 } /* end of child */
1519 /* parent continue to run more snapshot commands in parallel. */
1523 if (head == &si->si_osts_list) {
1524 head = &si->si_mdts_list;
1528 snapshot_wait(si, &rc);
1532 head = &si->si_osts_list;
1535 list_for_each_entry(st, head, st_list) {
1536 if (st->st_ignored && !si->si_force) {
1539 snapshot_role2name(name, st->st_role, st->st_index);
1541 "Miss snapshot piece on the %s. Use '-f' "
1542 "option if want to destroy it by force.\n",
1549 if (head == &si->si_osts_list) {
1550 head = &si->si_mdts_list;
1555 rc = snapshot_general_check(si);
1560 static int __snapshot_destroy(struct snapshot_instance *si,
1561 struct list_head *head)
1563 struct snapshot_target *st;
1567 list_for_each_entry(st, head, st_list) {
1576 SNAPSHOT_ADD_LOG(si, "Can't fork for destroy snapshot "
1577 "%s on the target (%s:%x:%d): %s\n",
1578 si->si_ssname, st->st_host,
1579 st->st_role, st->st_index,
1586 char cmd[MAX_BUF_SIZE];
1588 memset(cmd, 0, sizeof(cmd));
1590 snprintf(cmd, sizeof(cmd) - 1,
1591 "%s %s 'umount -f %s/%s@%s "
1592 "> /dev/null 2>&1; "
1593 "zfs destroy -f %s/%s@%s'",
1594 si->si_rsh, st->st_host, st->st_pool,
1595 st->st_filesystem, si->si_ssname,
1596 st->st_pool, st->st_filesystem,
1599 snprintf(cmd, sizeof(cmd) - 1,
1600 "%s %s 'zfs destroy %s/%s@%s'",
1601 si->si_rsh, st->st_host, st->st_pool,
1602 st->st_filesystem, si->si_ssname);
1603 rc = snapshot_exec(cmd);
1605 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on "
1606 "target (%s:%x:%d): rc = %d\n",
1607 cmd, st->st_host, st->st_role,
1611 } /* end of child */
1613 /* parent continue to run more snapshot commands in parallel. */
1620 static int snapshot_destroy(struct snapshot_instance *si)
1628 rc = snapshot_destroy_check(si);
1632 rc = snapshot_get_fsname(si, fsname, sizeof(fsname));
1636 /* 1.1 Destroy snapshot on every OST */
1637 rc = __snapshot_destroy(si, &si->si_osts_list);
1638 if (!si->si_force) {
1642 __snapshot_wait(si, &si->si_osts_list, &rc);
1647 /* 1.2 Destroy snapshot on every MDT */
1648 rc1 = __snapshot_destroy(si, &si->si_mdts_list);
1650 /* 2 Wait for all children, even though part of them maybe failed */
1651 snapshot_wait(si, &rc2);
1652 if (rc2 == -ENOENT && si->si_force)
1655 /* 3. Erase config llog from MGS */
1656 if ((!rc && !rc1 && !rc2) || si->si_force) {
1659 __argv[0] = "erase_lcfg";
1662 rc3 = jt_lcfg_erase(3, __argv);
1663 if (rc3 && errno == ENOENT)
1666 SNAPSHOT_ADD_LOG(si, "Can't erase config for destroy "
1667 "snapshot %s, fsname %s: rc = %d\n",
1668 si->si_ssname, fsname, rc3);
1671 return rc ? rc : (rc1 ? rc1 : (rc2 ? rc2 : rc3));
1674 int jt_snapshot_destroy(int argc, char **argv)
1676 struct snapshot_instance *si;
1677 struct option long_opts[] = {
1678 { .val = 'f', .name = "force", .has_arg = no_argument },
1679 { .val = 'F', .name = "fsname", .has_arg = required_argument },
1680 { .val = 'h', .name = "help", .has_arg = no_argument },
1681 { .val = 'n', .name = "name", .has_arg = required_argument },
1682 { .val = 'r', .name = "rsh", .has_arg = required_argument },
1686 si = snapshot_init(argc, argv, long_opts, "fF:hn:r:",
1687 snapshot_destroy_usage, LOCK_EX, &rc);
1691 if (!si->si_ssname) {
1693 "Miss the snapshot name to be destroyed\n");
1694 snapshot_destroy_usage();
1699 rc = snapshot_destroy(si);
1702 "Can't destroy the snapshot %s\n", si->si_ssname);
1703 SNAPSHOT_ADD_LOG(si, "Can't destroy snapshot %s with "
1704 "force <%s>: %d\n", si->si_ssname,
1705 si->si_force ? "enable" : "disable", rc);
1707 SNAPSHOT_ADD_LOG(si, "Destroy snapshot %s successfully "
1708 "with force <%s>\n", si->si_ssname,
1709 si->si_force ? "enable" : "disable");
1716 static void snapshot_modify_usage(void)
1719 "Change the specified snapshot's name and/or comment.\n"
1721 "snapshot_modify [-c | --comment comment] "
1722 "<-F | --fsname fsname> [-h | --help] "
1723 "<-n | --name ssname> [-N | --new new_ssname] "
1724 "[-r | --rsh remote_shell]\n"
1726 "-c: update the snapshot's comment.\n"
1727 "-F: the filesystem name.\n"
1728 "-h: for help information.\n"
1729 "-n: the snapshot's name.\n"
1730 "-N: rename the snapshot's name as the new_ssname.\n"
1731 "-r: the remote shell used for communication with remote "
1732 "target, the default value is 'ssh'.\n");
1735 static int snapshot_modify_check(struct snapshot_instance *si)
1739 if (si->si_new_ssname &&
1740 strcmp(si->si_ssname, si->si_new_ssname) == 0) {
1741 fprintf(stderr, "The new snapshot's name is the same as "
1742 "the old one %s %s.\n",
1743 si->si_ssname, si->si_new_ssname);
1747 if (!si->si_new_ssname && !si->si_comment) {
1748 fprintf(stderr, "Miss options, nothing to be changed.\n");
1752 rc = mdt0_is_lustre_snapshot(si);
1753 if (!rc && si->si_new_ssname) {
1754 rc = target_is_mounted(si, si->si_mdt0, si->si_ssname);
1757 "snapshot %s is mounted, can't be renamed.\n",
1766 static int __snapshot_modify(struct snapshot_instance *si,
1767 struct list_head *head, __u64 xtime)
1769 struct snapshot_target *st;
1773 list_for_each_entry(st, head, st_list) {
1780 SNAPSHOT_ADD_LOG(si, "Can't fork for modify snapshot "
1781 "(%s|%s, <%s>) on the target "
1783 si->si_ssname, si->si_new_ssname,
1784 si->si_comment, st->st_host,
1785 st->st_role, st->st_index,
1792 char cmd[MAX_BUF_SIZE];
1794 memset(cmd, 0, sizeof(cmd));
1795 if (si->si_new_ssname && si->si_comment)
1796 snprintf(cmd, sizeof(cmd) - 1,
1797 "%s %s 'zpool import -d %s %s > "
1799 "zfs rename %s/%s@%s %s/%s@%s && "
1800 "zfs set lustre:comment=\"%s\" "
1802 "zfs set lustre:mtime=%llu %s/%s@%s'",
1803 si->si_rsh, st->st_host,
1804 st->st_dir ? st->st_dir :
1806 st->st_pool, st->st_pool,
1807 st->st_filesystem, si->si_ssname,
1808 st->st_pool, st->st_filesystem,
1809 si->si_new_ssname, si->si_comment,
1810 st->st_pool, st->st_filesystem,
1811 si->si_new_ssname, xtime,
1812 st->st_pool, st->st_filesystem,
1814 else if (si->si_new_ssname)
1815 snprintf(cmd, sizeof(cmd) - 1,
1816 "%s %s 'zpool import -d %s %s > "
1818 "zfs rename %s/%s@%s %s/%s@%s && "
1819 "zfs set lustre:mtime=%llu %s/%s@%s'",
1820 si->si_rsh, st->st_host,
1821 st->st_dir ? st->st_dir :
1823 st->st_pool, st->st_pool,
1824 st->st_filesystem, si->si_ssname,
1825 st->st_pool, st->st_filesystem,
1826 si->si_new_ssname, xtime, st->st_pool,
1827 st->st_filesystem, si->si_new_ssname);
1828 else if (si->si_comment)
1829 snprintf(cmd, sizeof(cmd) - 1,
1830 "%s %s 'zpool import -d %s %s > "
1831 "/dev/null 2>&1; zfs set "
1832 "lustre:comment=\"%s\" %s/%s@%s && "
1833 "zfs set lustre:mtime=%llu %s/%s@%s'",
1834 si->si_rsh, st->st_host,
1835 st->st_dir ? st->st_dir :
1837 st->st_pool, si->si_comment,
1838 st->st_pool, st->st_filesystem,
1839 si->si_ssname, xtime, st->st_pool,
1840 st->st_filesystem, si->si_ssname);
1844 rc = snapshot_exec(cmd);
1846 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on "
1847 "target (%s:%x:%d): rc = %d\n",
1848 cmd, st->st_host, st->st_role,
1852 } /* end of child */
1854 /* parent continue to run more snapshot commands in parallel. */
1861 static int snapshot_modify(struct snapshot_instance *si)
1867 rc = snapshot_modify_check(si);
1873 /* Modify snapshot on every MDT */
1874 rc = __snapshot_modify(si, &si->si_mdts_list, (__u64)tt);
1876 /* Modify snapshot on every OST */
1877 rc = __snapshot_modify(si, &si->si_osts_list, (__u64)tt);
1879 /* Wait for all children, even though part of them maybe failed */
1880 snapshot_wait(si, &rc1);
1882 return rc ? rc : rc1;
1885 int jt_snapshot_modify(int argc, char **argv)
1887 struct snapshot_instance *si;
1888 struct option long_opts[] = {
1889 { .val = 'c', .name = "comment", .has_arg = required_argument },
1890 { .val = 'F', .name = "fsname", .has_arg = required_argument },
1891 { .val = 'h', .name = "help", .has_arg = no_argument },
1892 { .val = 'n', .name = "name", .has_arg = required_argument },
1893 { .val = 'N', .name = "new", .has_arg = required_argument },
1894 { .val = 'r', .name = "rsh", .has_arg = required_argument },
1898 si = snapshot_init(argc, argv, long_opts, "c:F:hn:N:r:",
1899 snapshot_modify_usage, LOCK_EX, &rc);
1903 if (!si->si_ssname) {
1905 "Miss the snapshot name to be modified\n");
1906 snapshot_modify_usage();
1911 rc = snapshot_modify(si);
1914 "Can't modify the snapshot %s\n", si->si_ssname);
1915 SNAPSHOT_ADD_LOG(si, "Can't modify snapshot %s with "
1916 "name <%s>, comment <%s>: %d\n",
1917 si->si_ssname, si->si_new_ssname,
1918 si->si_comment, rc);
1920 SNAPSHOT_ADD_LOG(si, "Modify snapshot %s successfully "
1921 "with name <%s>, comment <%s>\n",
1922 si->si_ssname, si->si_new_ssname,
1930 static void snapshot_list_usage(void)
1933 "List the specified snapshot or all snapshots.\n"
1935 "snapshot_list [-d | --detail] "
1936 "<-F | --fsname fsname> [-h | --help] "
1937 "[-n | --name ssname] [-r | --rsh remote_shell]\n"
1939 "-d: list every piece for the specified snapshot.\n"
1940 "-F: the filesystem name.\n"
1941 "-h: for help information.\n"
1942 "-n: the snapshot's name.\n"
1943 "-r: the remote shell used for communication with remote "
1944 "target, the default value is 'ssh'.\n");
1947 static int snapshot_list_one(struct snapshot_instance *si,
1948 struct snapshot_target *st)
1950 char buf[MAX_BUF_SIZE];
1954 memset(buf, 0, sizeof(buf));
1955 snprintf(buf, sizeof(buf) - 1,
1956 "%s %s \"zpool import -d %s %s > /dev/null 2>&1; "
1957 "zfs get all %s/%s@%s | grep lustre: | grep local$ | "
1958 "awk '{ \\$1=\\\"\\\"; \\$NF=\\\"\\\"; print \\$0 }' | "
1959 "sed -e 's/^ //'\"",
1960 si->si_rsh, st->st_host,
1961 st->st_dir ? st->st_dir : "/dev -d /tmp",
1962 st->st_pool, st->st_pool, st->st_filesystem, si->si_ssname);
1963 fp = popen(buf, "r");
1965 SNAPSHOT_ADD_LOG(si, "Popen fail to list one: %s\n",
1970 if (si->si_detail) {
1973 snapshot_role2name(name, st->st_role, st->st_index);
1974 printf("\nsnapshot_role: %s\n", name);
1977 while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
1981 if (strncmp(buf, "lustre:fsname",
1982 strlen("lustre:fsname")) == 0) {
1983 ptr = snapshot_first_skip_blank(buf);
1985 printf("snapshot_fsname: %s\n", ptr);
1989 if (strncmp(buf, "lustre:comment",
1990 strlen("lustre:comment")) == 0) {
1991 ptr = snapshot_first_skip_blank(buf);
1993 printf("comment: %s\n", ptr);
1997 if (strncmp(buf, "lustre:ctime",
1998 strlen("lustre:ctime")) == 0) {
1999 ptr = snapshot_first_skip_blank(buf);
2001 sscanf(ptr, "%llu", &xtime);
2002 printf("create_time: %s",
2003 ctime((time_t *)&xtime));
2008 if (strncmp(buf, "lustre:mtime",
2009 strlen("lustre:mtime")) == 0) {
2010 ptr = snapshot_first_skip_blank(buf);
2012 sscanf(ptr, "%llu", &xtime);
2013 printf("modify_time: %s",
2014 ctime((time_t *)&xtime));
2021 rc = target_is_mounted(si, st, si->si_ssname);
2023 printf("status: unknown\n");
2025 printf("status: not mount\n");
2027 printf("status: mounted\n");
2032 static int __snapshot_list(struct snapshot_instance *si,
2033 struct list_head *head)
2035 struct snapshot_target *st;
2038 list_for_each_entry(st, head, st_list) {
2041 rc1 = snapshot_list_one(si, st);
2042 if (rc1 < 0 || rc >= 0)
2049 static int snapshot_list(struct snapshot_instance *si)
2053 rc = snapshot_general_check(si);
2057 printf("\nfilesystem_name: %s\nsnapshot_name: %s\n",
2058 si->si_fsname, si->si_ssname);
2060 if (!si->si_detail) {
2061 rc = snapshot_list_one(si, si->si_mdt0);
2065 rc = __snapshot_list(si, &si->si_mdts_list);
2066 rc1 = __snapshot_list(si, &si->si_osts_list);
2071 return rc < 0 ? rc : 0;
2074 static int snapshot_list_all(struct snapshot_instance *si)
2076 struct list_sub_item {
2077 struct list_head lsi_list;
2081 struct list_head list_sub_items;
2082 struct list_sub_item *lsi;
2083 char buf[MAX_BUF_SIZE];
2087 INIT_LIST_HEAD(&list_sub_items);
2088 memset(buf, 0, sizeof(buf));
2089 snprintf(buf, sizeof(buf) - 1,
2090 "%s %s \"zfs get -H -r lustre:magic %s/%s | "
2091 "grep %s | awk '{ print \\$1 }' | cut -d@ -f2\"",
2092 si->si_rsh, si->si_mdt0->st_host, si->si_mdt0->st_pool,
2093 si->si_mdt0->st_filesystem, SNAPSHOT_MAGIC);
2094 fp = popen(buf, "r");
2096 SNAPSHOT_ADD_LOG(si, "Popen fail to list ssnames: %s\n",
2101 while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
2102 int len = strlen(buf);
2104 lsi = malloc(len + 1 + sizeof(struct list_sub_item));
2106 SNAPSHOT_ADD_LOG(si, "NOT enough memory\n");
2111 strncpy(lsi->lsi_ssname, buf, len);
2112 lsi->lsi_ssname[len] = '\0';
2113 list_add(&lsi->lsi_list, &list_sub_items);
2117 while (!list_empty(&list_sub_items)) {
2118 lsi = list_entry(list_sub_items.next,
2119 struct list_sub_item, lsi_list);
2120 list_del(&lsi->lsi_list);
2122 si->si_ssname = lsi->lsi_ssname;
2123 rc = snapshot_list(si);
2124 si->si_ssname = NULL;
2133 int jt_snapshot_list(int argc, char **argv)
2135 struct snapshot_instance *si;
2136 struct option long_opts[] = {
2137 { .val = 'd', .name = "detail", .has_arg = no_argument },
2138 { .val = 'F', .name = "fsname", .has_arg = required_argument },
2139 { .val = 'h', .name = "help", .has_arg = no_argument },
2140 { .val = 'n', .name = "name", .has_arg = required_argument },
2141 { .val = 'r', .name = "rsh", .has_arg = required_argument },
2145 si = snapshot_init(argc, argv, long_opts, "dF:hn:r:",
2146 snapshot_list_usage, LOCK_SH, &rc);
2151 rc = snapshot_list(si);
2153 rc = snapshot_list_all(si);
2157 "Can't list the snapshot %s\n", si->si_ssname);
2158 SNAPSHOT_ADD_LOG(si, "Can't list snapshot %s with detail "
2159 "<%s>: %d\n", si->si_ssname,
2160 si->si_detail ? "yes" : "no", rc);
2167 static void snapshot_mount_usage(void)
2170 "Mount the specified snapshot.\n"
2172 "snapshot_mount <-F | --fsname fsname> [-h | --help] "
2173 "<-n | --name ssname> "
2174 "[-r | --rsh remote_shell]\n"
2176 "-F: the filesystem name.\n"
2177 "-h: for help information.\n"
2178 "-n: the snapshot's name.\n"
2179 "-r: the remote shell used for communication with remote "
2180 "target, the default value is 'ssh'.\n");
2183 static int snapshot_mount_check(struct snapshot_instance *si, char *fsname,
2184 int fslen, bool *mgs_running)
2188 rc = mdt0_is_lustre_snapshot(si);
2192 rc = snapshot_get_fsname(si, fsname, fslen);
2196 rc = target_is_mounted(si, si->si_mgs, NULL);
2198 *mgs_running = true;
2205 static int snapshot_mount_target(struct snapshot_instance *si,
2206 struct snapshot_target *st, const char *optstr)
2208 char cmd[MAX_BUF_SIZE];
2212 rc = target_is_mounted(si, st, si->si_ssname);
2219 memset(cmd, 0, sizeof(cmd));
2220 memset(name, 0, sizeof(name));
2221 snapshot_role2name(name, st->st_role, st->st_index);
2222 snprintf(cmd, sizeof(cmd) - 1,
2223 "%s %s 'zpool import -d %s %s > /dev/null 2>&1; "
2224 "mkdir -p /mnt/%s_%s && mount -t lustre "
2225 "-o rdonly_dev%s %s/%s@%s /mnt/%s_%s'",
2226 si->si_rsh, st->st_host,
2227 st->st_dir ? st->st_dir : "/dev -d /tmp",
2228 st->st_pool, si->si_ssname, name,
2229 st != si->si_mdt0 ? "" : optstr,
2230 st->st_pool, st->st_filesystem, si->si_ssname,
2231 si->si_ssname, name);
2232 rc = snapshot_exec(cmd);
2234 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on the target "
2235 "(%s:%x:%d): rc = %d\n", cmd, st->st_host,
2236 st->st_role, st->st_index, rc);
2241 static int __snapshot_mount(struct snapshot_instance *si,
2242 struct list_head *head)
2244 struct snapshot_target *st;
2248 list_for_each_entry(st, head, st_list) {
2249 if (st == si->si_mgs || st == si->si_mdt0)
2258 SNAPSHOT_ADD_LOG(si, "Can't fork for mount snapshot "
2259 "%s on target (%s:%x:%d): %s\n",
2260 si->si_ssname, st->st_host,
2261 st->st_role, st->st_index,
2268 rc = snapshot_mount_target(si, st, "");
2272 /* parent continue to run more snapshot commands in parallel. */
2279 static int __snapshot_umount(struct snapshot_instance *si,
2280 struct list_head *head);
2282 static int snapshot_mount(struct snapshot_instance *si)
2284 struct snapshot_target *st;
2290 bool mdt0_mounted = false;
2291 bool mgs_running = false;
2293 rc = snapshot_mount_check(si, fsname, sizeof(fsname), &mgs_running);
2296 "Can't mount the snapshot %s: %s\n",
2297 si->si_ssname, strerror(-rc));
2301 /* 1. MGS is not mounted yet, mount the MGS firstly */
2302 si->si_mgs->st_ignored = 0;
2303 si->si_mgs->st_pid = 0;
2305 rc = snapshot_mount_target(si, si->si_mgs, "");
2307 si->si_mgs->st_ignored = 1;
2313 "Can't mount the snapshot %s: %s\n",
2314 si->si_ssname, strerror(-rc));
2318 if (si->si_mgs == si->si_mdt0)
2319 mdt0_mounted = true;
2322 /* 2. Mount MDT0 if it is not combined with the MGS. */
2323 if (!mdt0_mounted) {
2324 si->si_mdt0->st_ignored = 0;
2325 si->si_mdt0->st_pid = 0;
2326 rc = snapshot_mount_target(si, si->si_mdt0, ",nomgs");
2331 /* 3.1 Mount other MDTs in parallel */
2332 rc = __snapshot_mount(si, &si->si_mdts_list);
2334 /* 3.2 Mount OSTs in parallel */
2335 rc = __snapshot_mount(si, &si->si_osts_list);
2337 /* Wait for all children, even though part of them maybe failed */
2338 failed = snapshot_wait(si, &rc1);
2340 list_for_each_entry(st, &si->si_mdts_list, st_list) {
2341 if (!st->st_ignored)
2345 list_for_each_entry(st, &si->si_osts_list, st_list) {
2346 if (!st->st_ignored)
2354 __snapshot_umount(si, &si->si_mdts_list);
2355 __snapshot_umount(si, &si->si_osts_list);
2356 snapshot_wait(si, &rc2);
2360 "Can't mount the snapshot %s: %s\n",
2361 si->si_ssname, strerror(-rc));
2364 "%d of %d pieces of the snapshot %s "
2365 "can't be mounted: %s\n",
2366 failed, needed, si->si_ssname, strerror(-rc1));
2368 return rc ? rc : rc1;
2373 "The snapshot %s has already been mounted by other\n",
2378 fprintf(stdout, "mounted the snapshot %s with fsname %s\n",
2379 si->si_ssname, fsname);
2384 int jt_snapshot_mount(int argc, char **argv)
2386 struct snapshot_instance *si;
2387 struct option long_opts[] = {
2388 { .val = 'F', .name = "fsname", .has_arg = required_argument },
2389 { .val = 'h', .name = "help", .has_arg = no_argument },
2390 { .val = 'n', .name = "name", .has_arg = required_argument },
2391 { .val = 'r', .name = "rsh", .has_arg = required_argument },
2395 si = snapshot_init(argc, argv, long_opts, "F:hn:r:",
2396 snapshot_mount_usage, LOCK_EX, &rc);
2400 if (!si->si_ssname) {
2402 "Miss the snapshot name to be mounted\n");
2403 snapshot_mount_usage();
2408 rc = snapshot_mount(si);
2410 SNAPSHOT_ADD_LOG(si, "Can't mount snapshot %s: %d\n",
2413 SNAPSHOT_ADD_LOG(si, "The snapshot %s is mounted\n",
2421 static void snapshot_umount_usage(void)
2424 "Umount the specified snapshot.\n"
2426 "snapshot_umount <-F | --fsname fsname> [-h | --help] "
2427 "<-n | --name ssname> "
2428 "[-r | --rsh remote_shell]\n"
2430 "-F: the filesystem name.\n"
2431 "-h: for help information.\n"
2432 "-n: the snapshot's name.\n"
2433 "-r: the remote shell used for communication with remote "
2434 "target, the default value is 'ssh'.\n");
2437 static int __snapshot_umount(struct snapshot_instance *si,
2438 struct list_head *head)
2440 struct snapshot_target *st;
2444 list_for_each_entry(st, head, st_list) {
2451 SNAPSHOT_ADD_LOG(si, "Can't fork for umount snapshot "
2452 "%s on target (%s:%x:%d): %s\n",
2453 si->si_ssname, st->st_host,
2454 st->st_role, st->st_index,
2461 char cmd[MAX_BUF_SIZE];
2463 rc = target_is_mounted(si, st, si->si_ssname);
2470 memset(cmd, 0, sizeof(cmd));
2471 snprintf(cmd, sizeof(cmd) - 1,
2472 "%s %s 'umount %s/%s@%s'",
2473 si->si_rsh, st->st_host, st->st_pool,
2474 st->st_filesystem, si->si_ssname);
2475 rc = snapshot_exec(cmd);
2480 /* parent continue to run more snapshot commands in parallel. */
2487 static int snapshot_umount(struct snapshot_instance *si)
2489 struct snapshot_target *st;
2496 rc = snapshot_general_check(si);
2499 "Can't umount the snapshot %s: %s\n",
2500 si->si_ssname, strerror(-rc));
2504 rc = __snapshot_umount(si, &si->si_mdts_list);
2505 rc1 = __snapshot_umount(si, &si->si_osts_list);
2506 failed = snapshot_wait(si, &rc2);
2508 list_for_each_entry(st, &si->si_mdts_list, st_list) {
2509 if (!st->st_ignored)
2513 list_for_each_entry(st, &si->si_osts_list, st_list) {
2514 if (!st->st_ignored)
2520 "The snapshot %s has not been mounted\n",
2527 "%d of %d pieces of the snapshot %s "
2528 "can't be umounted: %s\n",
2529 failed, needed, si->si_ssname, strerror(-rc2));
2531 return rc ? rc : (rc1 ? rc1 : rc2);
2534 int jt_snapshot_umount(int argc, char **argv)
2536 struct snapshot_instance *si;
2537 struct option long_opts[] = {
2538 { .val = 'F', .name = "fsname", .has_arg = required_argument },
2539 { .val = 'h', .name = "help", .has_arg = no_argument },
2540 { .val = 'n', .name = "name", .has_arg = required_argument },
2541 { .val = 'r', .name = "rsh", .has_arg = required_argument },
2545 si = snapshot_init(argc, argv, long_opts, "F:hn:r:",
2546 snapshot_umount_usage, LOCK_EX, &rc);
2550 if (!si->si_ssname) {
2552 "Miss the snapshot name to be umounted\n");
2553 snapshot_umount_usage();
2558 rc = snapshot_umount(si);
2560 SNAPSHOT_ADD_LOG(si, "Can't umount snapshot %s: %d\n",
2563 SNAPSHOT_ADD_LOG(si, "the snapshot %s have been umounted\n",