Add option to set exact backup times

Change-Id: I6c1bf6216e215a70358f3ff84c8aa97c63acfd03
diff --git a/README.rst b/README.rst
index e782434..b7cd315 100644
--- a/README.rst
+++ b/README.rst
@@ -706,6 +706,46 @@
           full_backups_to_keep: 3
           hours_before_full: 24
 
+
+Backup client at exact times:
+
+..code-block:: yaml
+
+  ceph:
+    backup:
+      client:
+        enabled: true
+        full_backups_to_keep: 3
+        incr_before_full: 3
+        backup_times:
+          dayOfWeek: 0
+          hour: 4
+          minute: 52
+        compression: true
+        compression_threads: 2
+        database:
+          user: user
+          password: password
+        target:
+          host: host01
+
+  .. note:: Parameters in ``backup_times`` section can be used to set up exact
+  time the cron job should be executed. In this example, the backup job
+  would be executed every Sunday at 4:52 AM. If any of the individual
+  ``backup_times`` parameters is not defined, the defalut ``*`` value will be
+  used. For example, if minute parameter is ``*``, it will run the backup every minute,
+  which is ususally not desired.
+  Available parameters are ``dayOfWeek``, ``dayOfMonth``, ``month``, ``hour`` and ``minute``.
+  Please see the crontab reference for further info on how to set these parameters.
+
+  .. note:: Please be aware that only ``backup_times`` section OR
+  ``hours_before_full(incr)`` can be defined. If both are defined,
+  the ``backup_times`` section will be peferred.
+
+  .. note:: New parameter ``incr_before_full`` needs to be defined. This
+  number sets number of incremental backups to be run, before a full backup
+  is performed.
+
 Backup server rsync
 
 .. code-block:: yaml
@@ -729,6 +769,43 @@
       backup:
         restrict_clients: false
 
+Backup server at exact times:
+
+..code-block:: yaml
+
+  ceph:
+    backup:
+      server:
+        enabled: true
+        full_backups_to_keep: 3
+        incr_before_full: 3
+        backup_dir: /srv/backup
+        backup_times:
+          dayOfWeek: 0
+          hour: 4
+          minute: 52
+        key:
+          ceph_pub_key:
+            enabled: true
+            key: key
+
+  .. note:: Parameters in ``backup_times`` section can be used to set up exact
+  time the cron job should be executed. In this example, the backup job
+  would be executed every Sunday at 4:52 AM. If any of the individual
+  ``backup_times`` parameters is not defined, the defalut ``*`` value will be
+  used. For example, if minute parameter is ``*``, it will run the backup every minute,
+  which is ususally not desired.
+  Available parameters are ``dayOfWeek``, ``dayOfMonth``, ``month``, ``hour`` and ``minute``.
+  Please see the crontab reference for further info on how to set these parameters.
+
+  .. note:: Please be aware that only ``backup_times`` section OR
+  ``hours_before_full(incr)`` can be defined. If both are defined, The
+  ``backup_times`` section will be peferred.
+
+  .. note:: New parameter ``incr_before_full`` needs to be defined. This
+  number sets number of incremental backups to be run, before a full backup
+  is performed.
+
 Migration from Decapod to salt-formula-ceph
 --------------------------------------------
 
diff --git a/ceph/backup.sls b/ceph/backup.sls
index c4601e5..fb45576 100644
--- a/ceph/backup.sls
+++ b/ceph/backup.sls
@@ -40,8 +40,24 @@
 {%- if not backup.cron %}
   - commented: True
 {%- endif %}
-  - minute: random
-{%- if backup.client.hours_before_full is defined %}
+{%- if backup.client.backup_times is defined %}
+{%- if backup.client.backup_times.dayOfWeek is defined %}
+  - dayweek: {{ backup.client.backup_times.dayOfWeek }}
+{%- endif -%}
+{%- if backup.client.backup_times.month is defined %}
+  - month: {{ backup.client.backup_times.month }}
+{%- endif %}
+{%- if backup.client.backup_times.dayOfMonth is defined %}
+  - daymonth: {{ backup.client.backup_times.dayOfMonth }}
+{%- endif %}
+{%- if backup.client.backup_times.hour is defined %}
+  - hour: {{ backup.client.backup_times.hour }}
+{%- endif %}
+{%- if backup.client.backup_times.minute is defined %}
+  - minute: {{ backup.client.backup_times.minute }}
+{%- endif %}
+{%- elif backup.client.hours_before_incr is defined %}
+  - minute: '*'
 {%- if backup.client.hours_before_full <= 23 and backup.client.hours_before_full > 1 %}
   - hour: '*/{{ backup.client.hours_before_full }}'
 {%- elif not backup.client.hours_before_full <= 1 %}
@@ -134,8 +150,24 @@
 {%- if not backup.cron %}
   - commented: True
 {%- endif %}
-  - minute: random
-{%- if backup.server.hours_before_full is defined %}
+{%- if backup.server.backup_times is defined %}
+{%- if backup.server.backup_times.dayOfWeek is defined %}
+  - dayweek: {{ backup.server.backup_times.dayOfWeek }}
+{%- endif -%}
+{%- if backup.server.backup_times.month is defined %}
+  - month: {{ backup.server.backup_times.month }}
+{%- endif %}
+{%- if backup.server.backup_times.dayOfMonth is defined %}
+  - daymonth: {{ backup.server.backup_times.dayOfMonth }}
+{%- endif %}
+{%- if backup.server.backup_times.hour is defined %}
+  - hour: {{ backup.server.backup_times.hour }}
+{%- endif %}
+{%- if backup.server.backup_times.minute is defined %}
+  - minute: {{ backup.server.backup_times.minute }}
+{%- endif %}
+{%- elif backup.server.hours_before_incr is defined %}
+  - minute: '*'
 {%- if backup.server.hours_before_full <= 23 and backup.server.hours_before_full > 1 %}
   - hour: '*/{{ backup.server.hours_before_full }}'
 {%- elif not backup.server.hours_before_full <= 1 %}
diff --git a/ceph/files/backup/ceph-backup-client-runner-call.sh b/ceph/files/backup/ceph-backup-client-runner-call.sh
index 60e6c36..6c13525 100644
--- a/ceph/files/backup/ceph-backup-client-runner-call.sh
+++ b/ceph/files/backup/ceph-backup-client-runner-call.sh
@@ -3,6 +3,19 @@
 # Script to call ceph-backup-runner.sh in for loop to backup all keyspaces.
 # This script is also able to rsync backed up data to remote host and perform clean up on historical backups
 
+SKIPCLEANUP=false
+while getopts "sf" opt; do
+  case $opt in
+    s)
+      echo "Cleanup will be skipped" >&2
+      SKIPCLEANUP=true
+      ;;
+    \?)
+      echo "Invalid option: -$OPTARG" >&2
+      ;;
+  esac
+done
+
 # Configuration
 # -------------
     BACKUPDIR="{{ backup.backup_dir }}/full"
@@ -13,16 +26,16 @@
 
     SCRIPTDIR="/usr/local/bin"
     KEEP={{ backup.client.full_backups_to_keep }}
-    HOURSFULLBACKUPLIFE={{ backup.client.hours_before_full }} # Lifetime of the latest full backup in seconds
-    RSYNCLOGDIR="/var/log/backups"
-    RSYNCLOG="/var/log/backups/ceph-rsync.log"
-
-
+    {%- if backup.client.backup_times is not defined %}
+    HOURSFULLBACKUPLIFE={{ backup.client.hours_before_full }} # Lifetime of the latest full backup in hours
     if [ $HOURSFULLBACKUPLIFE -gt 24 ]; then
         FULLBACKUPLIFE=$(( 24 * 60 * 60 ))
     else
         FULLBACKUPLIFE=$(( $HOURSFULLBACKUPLIFE * 60 * 60 ))
     fi
+    {%- endif %}
+    RSYNCLOGDIR="/var/log/backups"
+    RSYNCLOG="/var/log/backups/ceph-rsync.log"
 
 # Functions
 # ---------
@@ -75,6 +88,25 @@
 
 # Cleanup
 # ---------
+if [ $SKIPCLEANUP = false ] ; then
+    {%- if backup.client.backup_times is not defined %}
+    echo "----------------------------"
     echo "Cleanup. Keeping only $KEEP full backups"
     AGE=$(($FULLBACKUPLIFE * $KEEP / 60))
     find $BACKUPDIR -maxdepth 1 -type d -mmin +$AGE -execdir echo "removing: "$BACKUPDIR/{} \; -execdir rm -rf $BACKUPDIR/{} \;
+    {%- else %}
+    echo "----------------------------"
+    echo "Cleanup. Keeping only $KEEP full backups"
+    NUMBER_OF_FULL=`find $BACKUPDIR -maxdepth 1 -mindepth 1 -type d -print| wc -l`
+    FULL_TO_DELETE=$(( $NUMBER_OF_FULL - $KEEP ))
+    if [ $FULL_TO_DELETE -gt 0 ] ; then
+        cd $BACKUPDIR
+        ls -t | tail -n -$FULL_TO_DELETE | xargs -d '\n' rm -rf
+    else
+        echo "There are less full backups than required, not deleting anything."
+    fi
+    {%- endif %}
+else
+    echo "----------------------------"
+    echo "-s parameter passed. Cleanup was not triggered"
+fi
\ No newline at end of file
diff --git a/ceph/files/backup/ceph-backup-server-runner.sh b/ceph/files/backup/ceph-backup-server-runner.sh
index 3186e8f..200897c 100644
--- a/ceph/files/backup/ceph-backup-server-runner.sh
+++ b/ceph/files/backup/ceph-backup-server-runner.sh
@@ -6,16 +6,31 @@
 
     BACKUPDIR="{{ backup.backup_dir }}/full"
     KEEP={{ backup.server.full_backups_to_keep }}
-    HOURSFULLBACKUPLIFE={{ backup.server.hours_before_full }} # Lifetime of the latest full backup in seconds
-
+    {%- if backup.server.backup_times is not defined %}
+    HOURSFULLBACKUPLIFE={{ backup.server.hours_before_full }} # Lifetime of the latest full backup in hours
     if [ $HOURSFULLBACKUPLIFE -gt 24 ]; then
         FULLBACKUPLIFE=$(( 24 * 60 * 60 ))
     else
         FULLBACKUPLIFE=$(( $HOURSFULLBACKUPLIFE * 60 * 60 ))
     fi
+    {%- endif %}
 
 # Cleanup
 # ---------
-    echo "Cleanup. Keeping only $KEEP full backups"
-    AGE=$(($FULLBACKUPLIFE * $KEEP / 60))
-    find $BACKUPDIR -maxdepth 1 -type d -mmin +$AGE -execdir echo "removing: "$BACKUPDIR/{} \; -execdir rm -rf $BACKUPDIR/{} \;
+{%- if backup.server.backup_times is not defined %}
+echo "----------------------------"
+echo "Cleanup. Keeping only $KEEP full backups"
+AGE=$(($FULLBACKUPLIFE * $KEEP / 60))
+find $BACKUPDIR -maxdepth 1 -type d -mmin +$AGE -execdir echo "removing: "$BACKUPDIR/{} \; -execdir rm -rf $BACKUPDIR/{} \;
+{%- else %}
+echo "----------------------------"
+echo "Cleanup. Keeping only $KEEP full backups"
+NUMBER_OF_FULL=`find $BACKUPDIR -maxdepth 1 -mindepth 1 -type d -print| wc -l`
+FULL_TO_DELETE=$(( $NUMBER_OF_FULL - $KEEP ))
+if [ $FULL_TO_DELETE -gt 0 ] ; then
+    cd $BACKUPDIR
+    ls -t | tail -n -$FULL_TO_DELETE | xargs -d '\n' rm -rf
+else
+    echo "There are less full backups than required, not deleting anything."
+fi
+{%- endif %}
\ No newline at end of file