Merge "Optimize kitchen tests for Travis CI"
diff --git a/README.rst b/README.rst
index 71f2197..9a10052 100644
--- a/README.rst
+++ b/README.rst
@@ -77,7 +77,7 @@
 
 * Cluster and public network
 
-Ceph cluster is accessed using network and thus you need to have decend capacity to handle all the client. There are two networks required for cluster: **public** network and cluster network. Public network is used for client connections and MONs and OSDs are listening on this network. Second network ic called **cluster** networks and this network is used for communication between OSDs. 
+Ceph cluster is accessed using network and thus you need to have decend capacity to handle all the client. There are two networks required for cluster: **public** network and cluster network. Public network is used for client connections and MONs and OSDs are listening on this network. Second network ic called **cluster** networks and this network is used for communication between OSDs.
 
 Both networks should have dedicated interfaces, bonding interfaces and dedicating vlans on bonded interfaces isn't allowed. Good practise is dedicate more throughput for the cluster network because cluster traffic is more important than client traffic.
 
@@ -644,6 +644,50 @@
         pool_read_bytes_threshold: 70000000
         pool_read_ops_threshold: 1000
 
+Ceph monitor backups
+--------------------
+
+Backup client with ssh/rsync remote host
+
+.. code-block:: yaml
+
+    ceph:
+      backup:
+        client:
+          enabled: true
+          full_backups_to_keep: 3
+          hours_before_full: 24
+          target:
+            host: cfg01
+
+
+Backup client with local backup only
+
+.. code-block:: yaml
+
+    ceph:
+      backup:
+        client:
+          enabled: true
+          full_backups_to_keep: 3
+          hours_before_full: 24
+
+Backup server rsync
+
+.. code-block:: yaml
+
+    ceph:
+      backup:
+        server:
+          enabled: true
+          hours_before_full: 24
+          full_backups_to_keep: 5
+          key:
+            ceph_pub_key:
+              enabled: true
+              key: ssh_rsa
+
+
 
 More information
 ================
diff --git a/ceph/backup.sls b/ceph/backup.sls
new file mode 100644
index 0000000..83faf48
--- /dev/null
+++ b/ceph/backup.sls
@@ -0,0 +1,133 @@
+{%- from "ceph/map.jinja" import backup with context %}
+
+{%- if backup.client is defined %}
+
+{%- if backup.client.enabled %}
+
+ceph_backup_client_packages:
+  pkg.installed:
+  - names: {{ backup.pkgs }}
+
+ceph_backup_runner_script:
+  file.managed:
+  - name: /usr/local/bin/ceph-backup-runner.sh
+  - source: salt://ceph/files/backup/ceph-backup-client-runner.sh
+  - template: jinja
+  - mode: 655
+  - require:
+    - pkg: ceph_backup_client_packages
+
+ceph_call_backup_runner_script:
+  file.managed:
+  - name: /usr/local/bin/ceph-backup-runner-call.sh
+  - source: salt://ceph/files/backup/ceph-backup-client-runner-call.sh
+  - template: jinja
+  - mode: 655
+  - require:
+    - pkg: ceph_backup_client_packages
+
+ceph_backup_dir:
+  file.directory:
+  - name: {{ backup.backup_dir }}/full
+  - user: root
+  - group: root
+  - makedirs: true
+
+ceph_backup_runner_cron:
+  cron.present:
+  - name: /usr/local/bin/ceph-backup-runner-call.sh
+  - user: root
+{%- if not backup.cron %}
+  - commented: True
+{%- endif %}
+  - minute: random
+{%- if backup.client.hours_before_full is defined %}
+{%- 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 %}
+  - hour: 2
+{%- endif %}
+{%- else %}
+  - hour: 2
+{%- endif %}
+  - require:
+    - file: ceph_backup_runner_script
+    - file: ceph_call_backup_runner_script
+
+
+{%- endif %}
+
+{%- endif %}
+
+{%- if backup.server is defined %}
+
+{%- if backup.server.enabled %}
+
+ceph_backup_server_packages:
+  pkg.installed:
+  - names: {{ backup.pkgs }}
+
+ceph_user:
+  user.present:
+  - name: ceph
+  - system: true
+  - home: {{ backup.backup_dir }}
+
+{{ backup.backup_dir }}/full:
+  file.directory:
+  - mode: 755
+  - user: ceph
+  - group: ceph
+  - makedirs: true
+  - require:
+    - user: ceph_user
+    - pkg: ceph_backup_server_packages
+
+{%- for key_name, key in backup.server.key.iteritems() %}
+
+{%- if key.get('enabled', False) %}
+
+ceph_key_{{ key.key }}:
+  ssh_auth.present:
+  - user: ceph
+  - name: {{ key.key }}
+  - require:
+    - file: {{ backup.backup_dir }}/full
+
+
+{%- endif %}
+
+{%- endfor %}
+
+ceph_server_script:
+  file.managed:
+  - name: /usr/local/bin/ceph-backup-runner.sh
+  - source: salt://ceph/files/backup/ceph-backup-server-runner.sh
+  - template: jinja
+  - mode: 655
+  - require:
+    - pkg: ceph_backup_server_packages
+
+ceph_server_cron:
+  cron.present:
+  - name: /usr/local/bin/ceph-backup-runner.sh
+  - user: ceph
+{%- if not backup.cron %}
+  - commented: True
+{%- endif %}
+  - minute: random
+{%- if backup.server.hours_before_full is defined %}
+{%- 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 %}
+  - hour: 2
+{%- endif %}
+{%- else %}
+  - hour: 2
+{%- endif %}
+  - require:
+    - file: ceph_server_script
+
+{%- endif %}
+
+{%- endif %}
diff --git a/ceph/common.sls b/ceph/common.sls
index 03acf12..874a444 100644
--- a/ceph/common.sls
+++ b/ceph/common.sls
@@ -1,25 +1,41 @@
 {%- from "ceph/map.jinja" import common with context %}
 
+{% if not common.get('container_mode', False) %}
+
 common_packages:
   pkg.installed:
   - names: {{ common.pkgs }}
 
+{%- endif %}
+
+{{ common.prefix_dir }}/etc/ceph:
+  file.directory:
+  - user: root
+  - group: root
+  - mode: 755
+  - makedirs: True
+
 common_config:
   file.managed:
-  - name: /etc/ceph/ceph.conf
+  - name: {{ common.prefix_dir }}/etc/ceph/ceph.conf
   - source: salt://ceph/files/{{ common.version }}/ceph.conf.{{ grains.os_family }}
   - template: jinja
+  {% if not common.get('container_mode', False) %}
   - require:
     - pkg: common_packages
+  {%- endif %}
+
 
 {%- if common.keyring is defined and common.keyring.admin is defined %}
 
 ceph_create_keyring_admin:
   cmd.run:
-  - name: "ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin {%- for cap_name, cap in  common.keyring.admin.caps.iteritems() %} --cap {{ cap_name }} '{{ cap }}' {%- endfor %}"
-  - unless: "test -f /etc/ceph/ceph.client.admin.keyring"
+  - name: "ceph-authtool --create-keyring {{ common.prefix_dir }}/etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin {%- for cap_name, cap in  common.keyring.admin.caps.iteritems() %} --cap {{ cap_name }} '{{ cap }}' {%- endfor %}"
+  - unless: "test -f {{ common.prefix_dir }}/etc/ceph/ceph.client.admin.keyring"
   - require:
+  {% if not common.get('container_mode', False) %}
     - pkg: common_packages
+  {%- endif %}
     - file: common_config
 
 {%- endif %}
@@ -30,15 +46,17 @@
 
 {%- if loop.index0 == 0 %}
 
-/etc/ceph/ceph.client.admin.keyring:
+{{ common.prefix_dir }}/etc/ceph/ceph.client.admin.keyring:
   file.managed:
   - source: salt://ceph/files/keyring
   - template: jinja
-  - unless: "test -f /etc/ceph/ceph.client.admin.keyring"
+  - unless: "test -f {{ common.prefix_dir }}/etc/ceph/ceph.client.admin.keyring"
   - defaults:
       node_grains: {{ node_grains|yaml }}
   - require:
+  {% if not common.get('container_mode', False) %}
     - pkg: common_packages
+  {%- endif %}
     - file: common_config
 
 {%- endif %}
diff --git a/ceph/files/backup/ceph-backup-client-runner-call.sh b/ceph/files/backup/ceph-backup-client-runner-call.sh
new file mode 100644
index 0000000..bc69521
--- /dev/null
+++ b/ceph/files/backup/ceph-backup-client-runner-call.sh
@@ -0,0 +1,79 @@
+{%- from "ceph/map.jinja" import backup with context -%}
+#!/bin/bash
+# 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
+
+# Configuration
+# -------------
+    BACKUPDIR="{{ backup.backup_dir }}/full"
+    TMPDIR="$( pwd )/tmp_ceph_backup"
+    HOSTNAME="$( hostname )"
+    TIMESTAMP="$( date +%m%d%k%M )"
+
+    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 [ $HOURSFULLBACKUPLIFE -gt 24 ]; then
+        FULLBACKUPLIFE=$(( 24 * 60 * 60 ))
+    else
+        FULLBACKUPLIFE=$(( $HOURSFULLBACKUPLIFE * 60 * 60 ))
+    fi
+
+# Functions
+# ---------
+    function check_dependencies() {
+        # Function to iterate through a list of required executables to ensure
+        # they are installed and executable by the current user.
+        DEPS="awk basename cp cqlsh date dirname echo find "
+        DEPS+="getopt grep hostname mkdir rm sed tail tar "
+        for bin in $DEPS; do
+            $( which $bin >/dev/null 2>&1 ) || NOTFOUND+="$bin "
+        done
+
+        if [ ! -z "$NOTFOUND" ]; then
+            printf "Error finding required executables: ${NOTFOUND}\n" >&2
+            exit 1
+        fi
+    }
+
+
+    # Need write access to local directory to create dump file
+    if [ ! -w $( pwd ) ]; then
+        printf "You must have write access to the current directory $( pwd )\n"
+        exit 1
+    fi
+
+    if [ ! -d "$RSYNCLOGDIR" ] && [ ! -e "$RSYNCLOG" ]; then
+        mkdir -p "$RSYNCLOGDIR"
+    fi
+
+    $SCRIPTDIR/ceph-backup-runner.sh
+
+# rsync just the new or modified backup files
+# ---------
+
+    {%- if backup.client.target is defined %}
+    echo "Adding ssh-key of remote host to known_hosts"
+    ssh-keygen -R {{ backup.client.target.host }} 2>&1 | > $RSYNCLOG
+    ssh-keyscan {{ backup.client.target.host }} >> ~/.ssh/known_hosts  2>&1 | >> $RSYNCLOG
+    echo "Rsyncing files to remote host"
+    /usr/bin/rsync -rhtPv --rsync-path=rsync --progress $BACKUPDIR/* -e ssh ceph@{{ backup.client.target.host }}:$BACKUPDIR >> $RSYNCLOG
+
+    # Check if the rsync succeeded or failed
+    if [ -s $RSYNCLOG ] && ! grep -q "rsync error: " $RSYNCLOG; then
+            echo "Rsync to remote host completed OK"
+    else
+            echo "Rsync to remote host FAILED"
+            exit 1
+    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/{} \;
diff --git a/ceph/files/backup/ceph-backup-client-runner.sh b/ceph/files/backup/ceph-backup-client-runner.sh
new file mode 100644
index 0000000..d2601eb
--- /dev/null
+++ b/ceph/files/backup/ceph-backup-client-runner.sh
@@ -0,0 +1,66 @@
+{%- from "ceph/map.jinja" import backup, mon, osd with context -%}
+#!/bin/bash
+# Script to backup ceph schema and create snapshot of keyspaces
+
+# Configuration
+# -------------
+    BACKUPDIR="{{ backup.backup_dir }}/full"
+    TMPDIR="$( pwd )/tmp_ceph_backup"
+    HOSTNAME="$( hostname )"
+    TIMESTAMP="$( date +%m%d%k%M )"
+
+    # Need write access to local directory to create dump file
+    if [ ! -w $( pwd ) ]; then
+        printf "You must have write access to the current directory $( pwd )\n"
+        exit 1
+    fi
+
+    # Create temporary working directory.  Yes, deliberately avoiding mktemp
+    if [ ! -d "$TMPDIR" ] && [ ! -e "$TMPDIR" ]; then
+        mkdir -p "$TMPDIR"
+    else
+        printf "Error creating temporary directory $TMPDIR"
+        exit 1
+    fi
+
+    if [ ! -d "$TMPDIR/ceph-$HOSTNAME" ] && [ ! -e "$TMPDIR/ceph-$HOSTNAME" ]; then
+        mkdir -p "$TMPDIR/ceph-$HOSTNAME"
+    else
+        printf "Error creating temporary directory $TMPDIR/ceph-$HOSTNAME"
+        exit 1
+    fi
+
+    # Create backup directory.
+    if [ ! -d "$BACKUPDIR" ] && [ ! -e "$BACKUPDIR" ]; then
+        mkdir -p "$BACKUPDIR"
+    fi
+
+    # Create Backup
+    # --------------------
+
+    mkdir -p "$BACKUPDIR/$HOSTNAME/"
+
+{%- if osd.get('enabled', False) %}
+    cp -a /etc/ceph/ $TMPDIR/
+    cp -a /var/lib/ceph/ $TMPDIR/ceph-$HOSTNAME/
+{%- elif mon.get('enabled', False) %}
+    cp -a /etc/ceph/ $TMPDIR/
+    service ceph-mon@$HOSTNAME stop
+    cp -a /var/lib/ceph/ $TMPDIR/ceph-$HOSTNAME/
+    service ceph-mon@$HOSTNAME start
+{%- endif %}
+
+    tar -cvzf $BACKUPDIR/$HOSTNAME/ceph-$HOSTNAME-$TIMESTAMP.tgz $TMPDIR
+    RC=$?
+
+    if [ $RC -gt 0 ]; then
+        printf "Error generating tar archive.\n"
+        [ "$TMPDIR" != "/" ] && rm -rf "$TMPDIR"
+        exit 1
+    else
+        printf "Successfully created backup\n"
+        [ "$TMPDIR" != "/" ] && rm -rf "$TMPDIR"
+        exit 0
+    fi
+
+# Fin.
diff --git a/ceph/files/backup/ceph-backup-server-runner.sh b/ceph/files/backup/ceph-backup-server-runner.sh
new file mode 100644
index 0000000..3186e8f
--- /dev/null
+++ b/ceph/files/backup/ceph-backup-server-runner.sh
@@ -0,0 +1,21 @@
+{%- from "ceph/map.jinja" import backup with context -%}
+#!/bin/bash
+
+# Script to erase old backups on ceph 'server role' node.
+# ---------
+
+    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 [ $HOURSFULLBACKUPLIFE -gt 24 ]; then
+        FULLBACKUPLIFE=$(( 24 * 60 * 60 ))
+    else
+        FULLBACKUPLIFE=$(( $HOURSFULLBACKUPLIFE * 60 * 60 ))
+    fi
+
+# 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/{} \;
diff --git a/ceph/files/grafana_dashboards/ceph_cluster_prometheus.json b/ceph/files/grafana_dashboards/ceph_cluster_prometheus.json
index 4f99242..2bd96bd 100644
--- a/ceph/files/grafana_dashboards/ceph_cluster_prometheus.json
+++ b/ceph/files/grafana_dashboards/ceph_cluster_prometheus.json
@@ -170,7 +170,7 @@
           "tableColumn": "",
           "targets": [
             {
-              "expr": "ceph_num_mon_quorum",
+              "expr": "avg(ceph_num_mon_quorum)",
               "format": "time_series",
               "interval": "",
               "intervalFactor": 1,
@@ -749,7 +749,7 @@
           "tableColumn": "",
           "targets": [
             {
-              "expr": "count_scalar(ceph_pool_usage_objects)",
+              "expr": "count(ceph_pool_usage_objects)",
               "format": "time_series",
               "interval": "",
               "intervalFactor": 1,
@@ -766,7 +766,7 @@
           "valueMaps": [
             {
               "op": "=",
-              "text": "N/A",
+              "text": "0",
               "value": "null"
             }
           ],
diff --git a/ceph/files/telegraf.conf b/ceph/files/telegraf.conf
index 3e8ad9b..2ce6c27 100644
--- a/ceph/files/telegraf.conf
+++ b/ceph/files/telegraf.conf
@@ -4,9 +4,13 @@
 {%- endif %}
   gather_admin_socket_stats = {{ values.gather_admin_socket_stats|lower }}
   gather_cluster_stats = {{ values.gather_cluster_stats|lower }}
+  gather_pool_loads = {{ values.gather_pool_loads|lower }}
 {%- if values.ceph_binary is defined %}
   ceph_binary = "{{ values.ceph_binary }}"
 {%- endif %}
+{%- if values.rados_binary is defined %}
+  rados_binary = "{{ values.rados_binary }}"
+{%- endif %}
 {%- if values.ceph_config is defined %}
   ceph_config = "{{ values.ceph_config }}"
 {%- endif %}
diff --git a/ceph/init.sls b/ceph/init.sls
index fbd14b9..638c6f5 100644
--- a/ceph/init.sls
+++ b/ceph/init.sls
@@ -3,6 +3,9 @@
 - ceph.common
 - ceph.setup.keyring
 {% endif %}
+{% if pillar.ceph.backup is defined %}
+- ceph.backup
+{% endif %}
 {% if pillar.ceph.mon is defined %}
 - ceph.mon
 {% endif %}
diff --git a/ceph/map.jinja b/ceph/map.jinja
index 0986380..4873230 100644
--- a/ceph/map.jinja
+++ b/ceph/map.jinja
@@ -3,6 +3,8 @@
 Debian:
   pkgs:
   - ceph-common
+  container_mode: False
+  prefix_dir: ''
   services: []
 {%- endload %}
 {% set common = salt['grains.filter_by'](common_defaults, merge=salt['pillar.get']('ceph:common')) %}
@@ -88,5 +90,24 @@
 {%- endload %}
 {% set monitoring = salt['grains.filter_by'](monitoring_defaults, merge=salt['pillar.get']('ceph:monitoring')) %}
 
+{%- load_yaml as backup_defaults %}
+
+backup:
+  Debian:
+    pkgs:
+    - rsync
+    backup_dir: '/var/backups/ceph'
+    cron: True
+  RedHat:
+    pkgs:
+    - rsync
+    backup_dir: '/var/backups/ceph'
+    cron: True
+
+{%- endload %}
+
+{% set backup  = salt['grains.filter_by'](backup_defaults['backup'], merge=salt['pillar.get']('ceph:backup', {})) %}
+
+
 {#- vim:ft=sls
 -#}
diff --git a/ceph/meta/telegraf.yml b/ceph/meta/telegraf.yml
index f544e37..b7628d0 100644
--- a/ceph/meta/telegraf.yml
+++ b/ceph/meta/telegraf.yml
@@ -9,6 +9,7 @@
       ceph_user: client.{{ monitoring.cluster_stats.ceph_user|default('admin') }}
       gather_admin_socket_stats: {{ monitoring.cluster_stats.gather_admin_socket_stats|default('false') }}
       gather_cluster_stats: {{ monitoring.cluster_stats.gather_cluster_stats|default('true') }}
+      gather_pool_loads: {{ monitoring.cluster_stats.gather_pool_loads|default('true') }}
 {%- if monitoring.cluster_stats.ceph_binary is defined %}
       ceph_binary: {{ monitoring.cluster_stats.ceph_binary }}
 {%- endif %}
@@ -28,6 +29,7 @@
       ceph_user: client.admin
       gather_admin_socket_stats: false
       gather_cluster_stats: true
+      gather_pool_loads: true
 {%- endif %}
 agent:
   input:
@@ -37,6 +39,7 @@
       ceph_user: client.{{ monitoring.cluster_stats.ceph_user|default('admin') }}
       gather_admin_socket_stats: {{ monitoring.cluster_stats.gather_admin_socket_stats|default('true') }}
       gather_cluster_stats: {{ monitoring.cluster_stats.gather_cluster_stats|default('false') }}
+      gather_pool_loads: {{ monitoring.cluster_stats.gather_pool_loads|default('false') }}
 {%- if monitoring.cluster_stats.ceph_binary is defined %}
       ceph_binary: {{ monitoring.cluster_stats.ceph_binary }}
 {%- endif %}
@@ -56,6 +59,7 @@
       ceph_user: client.admin
       gather_admin_socket_stats: true
       gather_cluster_stats: false
+      gather_pool_loads: false
 {%- endif %}
 
 {%- endif %}
@@ -68,6 +72,7 @@
 {%- if monitoring.node_stats is defined %}
       gather_admin_socket_stats: {{ monitoring.node_stats.gather_admin_socket_stats|default('true') }}
       gather_cluster_stats: {{ monitoring.node_stats.gather_cluster_stats|default('false') }}
+      gather_pool_loads: {{ monitoring.cluster_stats.gather_pool_loads|default('false') }}
 {%- if monitoring.node_stats.socket_dir is defined %}
       socket_dir: {{ monitoring.node_stats.socket_dir }}
 {%- endif %}
@@ -83,5 +88,6 @@
 {%- else %}
       gather_admin_socket_stats: true
       gather_cluster_stats: false
+      gather_pool_loads: false
 {%- endif %}
 {%- endif %}
diff --git a/ceph/setup/keyring.sls b/ceph/setup/keyring.sls
index 0c9ce20..aeafc70 100644
--- a/ceph/setup/keyring.sls
+++ b/ceph/setup/keyring.sls
@@ -1,5 +1,7 @@
 {%- from "ceph/map.jinja" import common with context %}
 
+{% if not common.get('container_mode', False) %}
+
 {# run only if ceph cluster is present #}
 {%- for node_name, node_grains in salt['mine.get']('ceph:common:keyring:admin', 'grains.items', 'pillar').iteritems() %}
 
@@ -13,7 +15,7 @@
 
 {%- if keyring.name != 'admin' and keyring.key is defined and common.get("manage_keyring", False) %}
 
-/etc/ceph/ceph.client.{{ keyring.name }}.keyring:
+{{ common.prefix_dir }}/etc/ceph/ceph.client.{{ keyring.name }}.keyring:
   file.managed:
   - source: salt://ceph/files/keyring
   - template: jinja
@@ -23,16 +25,16 @@
 
 ceph_import_keyring_{{ keyring.name }}:
   cmd.run:
-  - name: "ceph auth import -i /etc/ceph/ceph.client.{{ keyring.name }}.keyring"
+  - name: "ceph auth import -i {{ common.prefix_dir }}/etc/ceph/ceph.client.{{ keyring.name }}.keyring"
   - onchanges:
-    - file: /etc/ceph/ceph.client.{{ keyring.name }}.keyring
+    - file: {{ common.prefix_dir }}/etc/ceph/ceph.client.{{ keyring.name }}.keyring
 
 {%- elif keyring.name != 'admin' %}
 
 ceph_create_keyring_{{ keyring.name }}:
   cmd.run:
-  - name: "ceph auth get-or-create client.{{ keyring.name }} {%- for cap_name, cap in  keyring.caps.iteritems() %} {{ cap_name }} '{{ cap }}' {%- endfor %} > /etc/ceph/ceph.client.{{ keyring.name }}.keyring"
-  - unless: "test -f /etc/ceph/ceph.client.{{ keyring.name }}.keyring"
+  - name: "ceph auth get-or-create client.{{ keyring.name }} {%- for cap_name, cap in  keyring.caps.iteritems() %} {{ cap_name }} '{{ cap }}' {%- endfor %} > {{ common.prefix_dir }}/etc/ceph/ceph.client.{{ keyring.name }}.keyring"
+  - unless: "test -f {{ common.prefix_dir }}/etc/ceph/ceph.client.{{ keyring.name }}.keyring"
 
 {%- endif %}
 
@@ -40,7 +42,7 @@
 
 {%- if keyring_name != 'admin' and keyring.key is defined and common.get("manage_keyring", False) %}
 
-/etc/ceph/ceph.client.{{ keyring_name }}.keyring:
+{{ common.prefix_dir }}/etc/ceph/ceph.client.{{ keyring_name }}.keyring:
   file.managed:
   - source: salt://ceph/files/keyring
   - template: jinja
@@ -50,16 +52,16 @@
 
 ceph_import_keyring_{{ keyring_name }}:
   cmd.run:
-  - name: "ceph auth import -i /etc/ceph/ceph.client.{{ keyring_name }}.keyring"
+  - name: "ceph auth import -i {{ common.prefix_dir }}/etc/ceph/ceph.client.{{ keyring_name }}.keyring"
   - onchanges:
-    - file: /etc/ceph/ceph.client.{{ keyring_name }}.keyring
+    - file: {{ common.prefix_dir }}/etc/ceph/ceph.client.{{ keyring_name }}.keyring
 
 {%- elif keyring_name != 'admin' %}
 
 ceph_create_keyring_{{ keyring_name }}:
   cmd.run:
-  - name: "ceph auth get-or-create client.{{ keyring_name }} {%- for cap_name, cap in  keyring.caps.iteritems() %} {{ cap_name }} '{{ cap }}' {%- endfor %} > /etc/ceph/ceph.client.{{ keyring_name }}.keyring"
-  - unless: "test -f /etc/ceph/ceph.client.{{ keyring_name }}.keyring"
+  - name: "ceph auth get-or-create client.{{ keyring_name }} {%- for cap_name, cap in  keyring.caps.iteritems() %} {{ cap_name }} '{{ cap }}' {%- endfor %} > {{ common.prefix_dir }}/etc/ceph/ceph.client.{{ keyring_name }}.keyring"
+  - unless: "test -f {{ common.prefix_dir }}/etc/ceph/ceph.client.{{ keyring_name }}.keyring"
 
 {%- endif %}
 
@@ -71,4 +73,6 @@
 
 {%- endif %}
 
-{%- endfor %}
\ No newline at end of file
+{%- endfor %}
+
+{%- endif %}
diff --git a/metadata/service/backup/client.yml b/metadata/service/backup/client.yml
new file mode 100644
index 0000000..2c1b1fc
--- /dev/null
+++ b/metadata/service/backup/client.yml
@@ -0,0 +1,13 @@
+applications:
+- ceph
+parameters:
+  _param:
+    ceph_remote_backup_server: cfg01
+  ceph:
+    backup:
+      client:
+        enabled: true
+        full_backups_to_keep: 3
+        hours_before_full: 24
+        target:
+          host: ${_param:ceph_remote_backup_server}
diff --git a/metadata/service/backup/server.yml b/metadata/service/backup/server.yml
new file mode 100644
index 0000000..937e13d
--- /dev/null
+++ b/metadata/service/backup/server.yml
@@ -0,0 +1,15 @@
+applications:
+- ceph
+parameters:
+  _param:
+    ceph_backup_public_key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzLUiHKAjfFXiZ3fsgx35uXF6VivfC5WFafu4QMalxmj6W+s277oCfdWA8Du5f1wtQXM73VQ5nHkXhM2UIfUMarsyhXK+BxKVrcgEBNHdKlDytaecUPyuOxTDdGWhN/DPv5/vL8NYWweEYBbNbLgU0Td7Rvm52TUXKThIDjeF7XDxX4ShXWipBSwU4boOUBtR8KWfga8fsqeBN+eacuAQFR3MrrOfVvAuWW6Bsf047cmd+V6Qv0raoW73Nu4M/ZAdTsaR5k62a0cHsSRoi3hCmNRqw+CZaQi8prQU6t26eWPEtznjp5EkPF+LLh8LxUoCfWqWT+Lxe8QQwT1nx/LCN
+  ceph:
+    backup:
+      server:
+        enabled: true
+        hours_before_full: 24
+        full_backups_to_keep: 5
+        key:
+          ceph_pub_key:
+            enabled: true
+            key: ${_param:ceph_backup_public_key}
diff --git a/metadata/service/common/container.yml b/metadata/service/common/container.yml
new file mode 100644
index 0000000..78b7b0c
--- /dev/null
+++ b/metadata/service/common/container.yml
@@ -0,0 +1,8 @@
+applications:
+- ceph
+parameters:
+  ceph:
+    common:
+      enabled: true
+      container_mode: true
+      prefix_dir: ${_param:ceph_common_prefix_dir}
diff --git a/metadata/service/monitoring/cluster_stats.yml b/metadata/service/monitoring/cluster_stats.yml
index 02fc70a..85ae108 100644
--- a/metadata/service/monitoring/cluster_stats.yml
+++ b/metadata/service/monitoring/cluster_stats.yml
@@ -3,6 +3,8 @@
 classes:
 - service.ceph.support
 parameters:
+  _param:
+    ceph_monitoring_user: admin
   ceph:
     monitoring:
       cluster_stats:
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 29fb975..4ee4e0f 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -124,7 +124,7 @@
 }
 
 salt_run() {
-    [ -e ${VEN_DIR}/bin/activate ] && source ${VENV_DIR}/bin/activate
+    [ -e ${VENV_DIR}/bin/activate ] && source ${VENV_DIR}/bin/activate
     salt-call ${SALT_OPTS} $*
 }