Additions to 'hammer pfs-*':
authorMatthew Dillon <dillon@dragonflybsd.org>
Wed, 24 Sep 2008 01:42:50 +0000 (01:42 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Wed, 24 Sep 2008 01:42:50 +0000 (01:42 +0000)
    * Add 'snapshots=dir' and 'snapshots-clear' options

      This allows you to specify the snapshots directory for any PFS, master
      or slave.  The option will override the default "snapshots"
      sub-directory used by the 'hammer cleanup' directive for PFS masters.

      This option is required for PFS slaves since the hammer cleanup
      directive cannot use a directory in the slave PFS as a working
      directory (because its a slave).

Additions to 'hammer cleanup':

    * Implement the snapshot limit found the config file.  e.g. if the
      <fs>/snapshots/config file contains: 'snapshots 1d 60d' then
      the filesystem is snapshotted once a day and snapshot softlinks over
      60 days old will be deleted.

    * This directive now checks the snapshots path configured in the PFS
      and uses it if it is not empty.  The snapshots path must be configured
      for slaves.

sbin/hammer/cmd_cleanup.c
sbin/hammer/cmd_pseudofs.c
sbin/hammer/hammer.8

index be97898..47bc194 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sbin/hammer/cmd_cleanup.c,v 1.3 2008/09/20 07:08:06 dillon Exp $
+ * $DragonFly: src/sbin/hammer/cmd_cleanup.c,v 1.4 2008/09/24 01:42:50 dillon Exp $
  */
 /*
  * Clean up a specific HAMMER filesystem or all HAMMER filesystems.
@@ -69,6 +69,9 @@ static int check_period(const char *snapshots_path, const char *cmd, int arg1,
 static void save_period(const char *snapshots_path, const char *cmd,
                        time_t savet);
 static int check_softlinks(const char *snapshots_path);
+static void cleanup_softlinks(const char *path, const char *snapshots_path,
+                       int arg2);
+static int check_expired(const char *fpath, int arg2);
 
 static int cleanup_snapshots(const char *path, const char *snapshots_path,
                              int arg1, int arg2);
@@ -185,16 +188,34 @@ do_cleanup(const char *path)
        FirstPFS = didpfs;
        didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid;
 
+       /*
+        * Figure out where the snapshot directory is.
+        */
+       if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') {
+               asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots);
+       } else if (mrec_tmp.pfs.pfsd.snapshots[0]) {
+               printf(" WARNING: pfs-slave's snapshots dir is not absolute\n");
+               return;
+       } else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) {
+               printf(" WARNING: must configure snapshot dir for PFS slave\n");
+               printf("\tWe suggest <fs>/var/slaves/<name> where "
+                      "<fs> is the base HAMMER fs\n");
+               printf("\tContaining the slave\n");
+               return;
+       } else {
+               asprintf(&snapshots_path,
+                        "%s%ssnapshots", path, dividing_slash(path));
+       }
+
        /*
         * Create a snapshot directory if necessary, and a config file if
         * necessary.
         */
-       asprintf(&snapshots_path, "%s%ssnapshots", path, dividing_slash(path));
        if (stat(snapshots_path, &st) < 0) {
                if (mkdir(snapshots_path, 0755) != 0) {
                        free(snapshots_path);
-                       printf(" unable to create snapshot dir: %s\n",
-                               strerror(errno));
+                       printf(" unable to create snapshot dir \"%s\": %s\n",
+                               snapshots_path, strerror(errno));
                        return;
                }
        }
@@ -226,7 +247,7 @@ do_cleanup(const char *path)
                return;
        }
 
-       printf(" processing PFS #%d\n", pfs.pfs_id);
+       printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path);
 
        /*
         * Process the config file
@@ -259,6 +280,7 @@ do_cleanup(const char *path)
                if (strcmp(cmd, "snapshots") == 0) {
                        if (check_period(snapshots_path, cmd, arg1, &savet)) {
                                printf("run\n");
+                               cleanup_softlinks(path, snapshots_path, arg2);
                                r = cleanup_snapshots(path, snapshots_path,
                                                  arg1, arg2);
                        } else {
@@ -435,6 +457,9 @@ save_period(const char *snapshots_path, const char *cmd,
        remove(ncheck_path);
 }
 
+/*
+ * Simply count the number of softlinks in the snapshots dir
+ */
 static int
 check_softlinks(const char *snapshots_path)
 {
@@ -444,10 +469,6 @@ check_softlinks(const char *snapshots_path)
        char *fpath;
        int res = 0;
 
-       /*
-        * Force snapshots_disabled to 0 if the snapshots directory
-        * contains softlinks.
-        */
        if ((dir = opendir(snapshots_path)) != NULL) {
                while ((den = readdir(dir)) != NULL) {
                        if (den->d_name[0] == '.')
@@ -462,6 +483,72 @@ check_softlinks(const char *snapshots_path)
        return(res);
 }
 
+/*
+ * Clean up expired softlinks in the snapshots dir
+ */
+static void
+cleanup_softlinks(const char *path __unused, const char *snapshots_path, int arg2)
+{
+       struct dirent *den;
+       struct stat st;
+       DIR *dir;
+       char *fpath;
+
+       if ((dir = opendir(snapshots_path)) != NULL) {
+               while ((den = readdir(dir)) != NULL) {
+                       if (den->d_name[0] == '.')
+                               continue;
+                       asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
+                       if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) &&
+                           strncmp(den->d_name, "snap-", 5) == 0) {
+                               if (check_expired(den->d_name, arg2)) {
+                                       if (VerboseOpt) {
+                                               printf("    expire %s\n",
+                                                       fpath);
+                                       }
+                                       remove(fpath);
+                               }
+                       }
+                       free(fpath);
+               }
+               closedir(dir);
+       }
+}
+
+/*
+ * Take a softlink path in the form snap-yyyymmdd-hhmm and the
+ * expiration in seconds (arg2) and return non-zero if the softlink
+ * has expired.
+ */
+static int
+check_expired(const char *fpath, int arg2)
+{
+       struct tm tm;
+       time_t t;
+       int year;
+       int month;
+       int day;
+       int hour;
+       int minute;
+       int r;
+
+       r = sscanf(fpath, "snap-%4d%2d%2d-%2d%2d",
+                  &year, &month, &day, &hour, &minute);
+       if (r == 5) {
+               bzero(&tm, sizeof(tm));
+               tm.tm_isdst = -1;
+               tm.tm_min = minute;
+               tm.tm_hour = hour;
+               tm.tm_mday = day;
+               tm.tm_mon = month - 1;
+               tm.tm_year = year - 1900;
+               t = time(NULL) - mktime(&tm);
+               if ((int)t > arg2)
+                       return(1);
+       }
+       return(0);
+}
+
 /*
  * Issue a snapshot.
  */
@@ -471,7 +558,7 @@ cleanup_snapshots(const char *path __unused, const char *snapshots_path,
 {
        int r;
 
-       runcmd(&r, "hammer snapshot %s", snapshots_path);
+       runcmd(&r, "hammer snapshot %s %s", path, snapshots_path);
        return(r);
 }
 
index 21b5419..6f0f0eb 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sbin/hammer/cmd_pseudofs.c,v 1.10 2008/08/21 23:28:43 thomas Exp $
+ * $DragonFly: src/sbin/hammer/cmd_pseudofs.c,v 1.11 2008/09/24 01:42:50 dillon Exp $
  */
 
 #include "hammer.h"
@@ -474,10 +474,19 @@ dump_pfsd(hammer_pseudofs_data_t pfsd)
                printf("    slave\n");
        }
        printf("    label=\"%s\"\n", pfsd->label);
-       if (pfsd->mirror_flags & HAMMER_PFSD_SLAVE)
+       if (pfsd->snapshots[0])
+               printf("    snapshots=\"%s\"\n", pfsd->snapshots);
+       if (pfsd->mirror_flags & HAMMER_PFSD_SLAVE) {
                printf("    operating as a SLAVE\n");
-       else
+               if (pfsd->snapshots[0] == 0)
+                       printf("    snapshots directory not set for slave\n");
+       } else {
                printf("    operating as a MASTER\n");
+               if (pfsd->snapshots[0] == 0) {
+                       printf("    snapshots dir for master "
+                              "defaults to <fs>/snapshots\n");
+               }
+       }
 }
 
 static void
@@ -524,6 +533,29 @@ parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd)
                                exit(1);
                        }
                        snprintf(pfsd->label, sizeof(pfsd->label), "%s", ptr);
+               } else if (strcmp(cmd, "snapshots") == 0) {
+                       len = strlen(ptr);
+                       if (ptr[0] != '/') {
+                               fprintf(stderr,
+                                       "option %s: '%s' must be an "
+                                       "absolute path\n", cmd, ptr);
+                               if (ptr[0] == 0) {
+                                       fprintf(stderr, 
+                                               "use 'snapshots-clear' "
+                                               "to unset snapshots dir\n");
+                               }
+                               exit(1);
+                       }
+                       if (len >= (int)sizeof(pfsd->snapshots)) {
+                               fprintf(stderr,
+                                       "option %s: path too long, %d "
+                                       "character limit\n", cmd, len);
+                               exit(1);
+                       }
+                       snprintf(pfsd->snapshots, sizeof(pfsd->snapshots),
+                                "%s", ptr);
+               } else if (strcmp(cmd, "snapshots-clear") == 0) {
+                       pfsd->snapshots[0] = 0;
                } else {
                        fprintf(stderr, "invalid option: %s\n", cmd);
                        exit(1);
@@ -557,6 +589,8 @@ pseudofs_usage(int code)
                "    shared-uuid=0x16llx\n"
                "    unique-uuid=0x16llx\n"
                "    label=\"string\"\n"
+               "    snapshots=\"/path\"\n"
+               "    snapshots-clear\n"
        );
        exit(code);
 }
index 8332ea5..28053fe 100644 (file)
@@ -30,7 +30,7 @@
 .\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\" 
-.\" $DragonFly: src/sbin/hammer/hammer.8,v 1.47 2008/09/20 04:23:21 dillon Exp $
+.\" $DragonFly: src/sbin/hammer/hammer.8,v 1.48 2008/09/24 01:42:50 dillon Exp $
 .Dd July 27, 2008
 .Dt HAMMER 8
 .Os
@@ -215,8 +215,8 @@ may continue to run in the background for a few seconds until the HAMMER
 ioctl detects the interrupt.
 .Pp
 Work on this command is still in progress.
-Expected additions:  Limit the number of snapshots to 60 days by default
-and remove snapshots as the filesystem becomes full.
+Expected additions:  An ability to remove snapshots dynamically as the
+filesystem becomes full.
 .\" ==== prune ====
 .It Ar prune Ar softlink-dir
 Prune the file system based on previously created snapshot softlinks.
@@ -463,6 +463,23 @@ Set the unique UUID for this file system.  This UUID should not be used
 anywhere else, even on exact copies of the file system.
 .It label=<string>
 Set a descriptive label for this file system.
+.It snapshots=<string>
+Specify the snapshots directory which 'hammer cleanup' will use to manage
+this PFS.  The snapshots directory does not need to be configured for 
+PFS masters and will default to "<fs>/snapshots".
+.Pp
+PFS slaves are mirroring slaves so you cannot configure a snapshots
+directory on the slave itself to be managed by the slave's machine.
+In fact, the slave will likely have a 'snapshots' sub-directory mirrored
+from the master, but that directory contains the configuration the master
+is using for its copy of the filesystem, not the configuration that we
+want to use for our slave.
+.Pp
+It is recommended that "/var/slaves/<name>" be configured for a PFS
+slave, where <name> is an appropriate label.  You can control snapshot
+retention on your slave independant of the master. 
+.It snapshots-clear
+Zero out the snapshots directory path for this PFS.
 .El
 .\" ==== pfs-upgrade ====
 .It Ar pfs-upgrade Ar dirpath