Add policy management for gnocchi

PROD-34861

Change-Id: Ifd68a957a8829f098ac635ae72a494b86d29ccd6
diff --git a/.kitchen.yml b/.kitchen.yml
index e460dd4..69bce0e 100644
--- a/.kitchen.yml
+++ b/.kitchen.yml
@@ -27,6 +27,10 @@
       repo: git
       source: https://gerrit.mcp.mirantis.com/salt-formulas/oslo-templates
       branch: <%=ENV['GERRIT_BRANCH'] || 'master' %>
+    - name: keystone
+      repo: git
+      source: https://gerrit.mcp.mirantis.com/salt-formulas/keystone
+      branch: <%=ENV['GERRIT_BRANCH'] || 'master' %>
   state_top:
     base:
       "*":
diff --git a/README.rst b/README.rst
index 8b27119..a4f905d 100644
--- a/README.rst
+++ b/README.rst
@@ -262,6 +262,19 @@
                   test_policy_rule2:
                     metric_pattern: 'foo2.*'
 
+Configuration of policy.json file:
+
+.. code-block:: yaml
+
+  gnocchi:
+    server:
+      ...
+      policy:
+        'resource_owner': 'project_id:%(project_id)s'
+        'get status': 'role:admin'
+        # Add key without value to remove line from policy.json
+        'list resource type':
+
 =======
 Gnocchi logging configuration
 ----------------------------------
diff --git a/gnocchi/map.jinja b/gnocchi/map.jinja
index c0f5468..89343ec 100644
--- a/gnocchi/map.jinja
+++ b/gnocchi/map.jinja
@@ -17,6 +17,8 @@
   - gnocchi-metricd
   identity:
     engine: keystone
+  oslo_policy:
+    policy_file: 'policy.json'
 
 {%- endload %}
 
diff --git a/gnocchi/server.sls b/gnocchi/server.sls
index 14f4af8..53f53f6 100644
--- a/gnocchi/server.sls
+++ b/gnocchi/server.sls
@@ -39,4 +39,38 @@
   - watch:
     - gnocchi_common_conf
 
+/etc/gnocchi/{{ server.get('oslo_policy', {}).get('policy_file', 'policy.json') }}:
+  file.managed:
+    - mode: 0640
+    - user: root
+    - group: gnocchi
+    - require:
+      - pkg: gnocchi_server_packages
+
+{%- for name, rule in server.get('policy', {}).iteritems() %}
+
+  {%- if rule != None %}
+gnocchi_keystone_rule_{{ name }}_present:
+  keystone_policy.rule_present:
+  - path: /etc/gnocchi/{{ server.get('oslo_policy', {}).get('policy_file', 'policy.json') }}
+  - name: '{{ name }}'
+  - rule: '{{ rule }}'
+  - require:
+    - pkg: gnocchi_server_packages
+    - file: /etc/gnocchi/{{ server.get('oslo_policy', {}).get('policy_file', 'policy.json') }}
+
+  {%- else %}
+
+gnocchi_keystone_rule_{{ name }}_absent:
+  keystone_policy.rule_absent:
+  - path: /etc/gnocchi/{{ server.get('oslo_policy', {}).get('policy_file', 'policy.json') }}
+  - name: '{{ name }}'
+  - require:
+    - pkg: gnocchi_server_packages
+    - file: /etc/gnocchi/{{ server.get('oslo_policy', {}).get('policy_file', 'policy.json') }}
+
+  {%- endif %}
+
+{%- endfor %}
+
 {%- endif %}
diff --git a/tests/pillar/server_cluster.sls b/tests/pillar/server_cluster.sls
index 39738f8..2d976ce 100644
--- a/tests/pillar/server_cluster.sls
+++ b/tests/pillar/server_cluster.sls
@@ -89,6 +89,10 @@
         secret_key: secret
     metricd:
       workers: 5
+    policy:
+      'resource_owner': 'project_id:%(project_id)s'
+      'get status': 'role:admin'
+      'list resource type':
   statsd:
     resource_id: 07f26121-5777-48ba-8a0b-d70468133dd9
     enabled: true
diff --git a/tests/pillar/server_single.sls b/tests/pillar/server_single.sls
index d152355..c628cb2 100644
--- a/tests/pillar/server_single.sls
+++ b/tests/pillar/server_single.sls
@@ -56,6 +56,10 @@
         port: 11211
     metricd:
       workers: 5
+    policy:
+      'resource_owner': 'project_id:%(project_id)s'
+      'get status': 'role:admin'
+      'list resource type':
   statsd:
     resource_id: 07f26121-5777-48ba-8a0b-d70468133dd9
     enabled: true
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index d7f2242..4665d34 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1,9 +1,15 @@
 #!/usr/bin/env bash
 
 ###
+# Script source: https://gerrit.mcp.mirantis.com/#/admin/projects/salt-formulas/cookiecutter-salt-formula
 # Script requirments:
 #apt-get install -y python-yaml virtualenv git
 
+__ScriptVersion="2019.01.07"
+__ScriptName="run_tests.sh"
+__ScriptFullName="$0"
+__ScriptArgs="$*"
+
 set -e
 [ -n "$DEBUG" ] && set -x
 
@@ -28,6 +34,8 @@
 
 SALT_OPTS="${SALT_OPTS} --retcode-passthrough --local -c ${SALT_CONFIG_DIR} --log-file=/dev/null"
 
+IGNORE_MODELVALIDATE_MASK=${IGNORE_MODELVALIDATE_MASK:-"novalidate"}
+
 if [ "x${SALT_VERSION}" != "x" ]; then
     PIP_SALT_VERSION="==${SALT_VERSION}"
 fi
@@ -47,9 +55,8 @@
     virtualenv $VENV_DIR
     source ${VENV_DIR}/bin/activate
     python -m pip install salt${PIP_SALT_VERSION}
-    python -m pip install jsonschema
-    if [[ -f ${CURDIR}/pip_requirements.txt ]]; then
-       python -m pip install -r ${CURDIR}/pip_requirements.txt
+    if [[ -f ${CURDIR}/test-requirements.txt ]]; then
+       python -m pip install -r ${CURDIR}/test-requirements.txt
     fi
 }
 
@@ -70,6 +77,12 @@
         state_name=$(basename ${pillar%.sls})
         echo -e "  ${state_name}:\n    - ${state_name}" >> ${SALT_PILLAR_DIR}/top.sls
     done
+    for pillar in $(find $PILLARDIR  -mindepth 2 -type f -iname *.sls); do
+        state_name=$(basename "${pillar%*.sls}")
+        os_release=$(echo $pillar | rev | cut -d'/' -f2 | rev)
+        grep ${FORMULA_NAME}: ${pillar} &>/dev/null || continue
+        echo -e "  ${os_release}_${state_name}:\n    - ${os_release}.${state_name}" >> ${SALT_PILLAR_DIR}/top.sls
+    done
 }
 
 setup_salt() {
@@ -84,6 +97,12 @@
         state_name=$(basename ${pillar%.sls})
         echo -e "  ${state_name}:\n    - ${FORMULA_NAME}" >> ${SALT_FILE_DIR}/top.sls
     done
+    for pillar in $(find $PILLARDIR  -mindepth 2 -type f -iname *.sls); do
+        state_name=$(basename "${pillar%*.sls}")
+        os_release=$(echo $pillar | rev | cut -d'/' -f2 | rev)
+        grep ${FORMULA_NAME}: ${pillar} &>/dev/null || continue
+        echo -e "  ${os_release}_${state_name}:\n    - ${FORMULA_NAME}" >> ${SALT_FILE_DIR}/top.sls
+    done
 
     cat << EOF > ${SALT_CONFIG_DIR}/minion
 file_client: local
@@ -155,19 +174,24 @@
 }
 
 prepare() {
-    [ -d ${BUILDDIR} ] && mkdir -p ${BUILDDIR}
+    if [[ -f ${BUILDDIR}/.prepare_done ]]; then
+      log_info "${BUILDDIR}/.prepare_done exist, not rebuilding BUILDDIR"
+      return
+    fi
+    [[ -d ${BUILDDIR} ]] && mkdir -p ${BUILDDIR}
 
     [[ ! -f "${VENV_DIR}/bin/activate" ]] && setup_virtualenv
     setup_mock_bin
     setup_pillar
     setup_salt
     install_dependencies
+    link_modules
+    touch ${BUILDDIR}/.prepare_done
 }
 
 lint_releasenotes() {
     [[ ! -f "${VENV_DIR}/bin/activate" ]] && setup_virtualenv
     source ${VENV_DIR}/bin/activate
-    python -m pip install reno
     reno lint ${CURDIR}/../
 }
 
@@ -204,22 +228,37 @@
 }
 
 run_model_validate(){
-    if [ -d ${SCHEMARDIR} ]; then
-      # model validator require py modules
-      fetch_dependency "salt:https://github.com/salt-formulas/salt-formula-salt"
-      link_modules
-      # Rendered Example:
-      # python $(which salt-call) --local -c /test1/maas/tests/build/salt --id=maas_cluster modelschema.model_validate maas cluster
-      for role in ${SCHEMARDIR}/*.yaml; do
-          state_name=$(basename "${role%*.yaml}")
-          minion_id="${state_name}"
-          # in case debug-reruns, usefull to make cleanup
-          [ -n "$DEBUG" ] && { salt_run saltutil.clear_cache; salt_run saltutil.refresh_pillar; salt_run saltutil.sync_all; }
-          salt_run -m ${DEPSDIR}/salt-formula-salt --id=${minion_id} modelschema.model_validate ${FORMULA_NAME} ${state_name} || { log_err "Execution of ${FORMULA_NAME}.${state_name} failed"; exit 1 ; }
+  # Run modelschema.model_validate validation.
+  # TEST iterateble, run for `each formula ROLE against each ROLE_PILLARNAME`
+  # Pillars should be named in conviend ROLE_XXX.sls or ROLE.sls
+  # Example:
+  # client.sls  client_auth.sls  server.sls  server_auth.sls
+  if [ -d ${SCHEMARDIR} ]; then
+    # model validator require py modules
+    fetch_dependency "salt:https://github.com/salt-formulas/salt-formula-salt"
+    link_modules
+    salt_run saltutil.clear_cache; salt_run saltutil.refresh_pillar; salt_run saltutil.sync_all;
+    for role in $(find $SCHEMARDIR/* -maxdepth 0 -type f -iname *.yaml); do
+      role_name=$(basename "${role%*.yaml}")
+      for pillar in $(ls pillar/${role_name}*.sls | grep -v ${IGNORE_MODELVALIDATE_MASK} ); do
+        pillar_name=$(basename "${pillar%*.sls}")
+        local _message="FORMULA:${FORMULA_NAME} ROLE:${role_name} against PILLAR:${pillar_name}"
+        log_info "model_validate ${_message}"
+        # Rendered Example:
+        # python $(which salt-call) --local -c /test1/maas/tests/build/salt --id=maas_cluster modelschema.model_validate maas cluster
+        salt_run -m ${DEPSDIR}/salt-formula-salt --id=${pillar_name} modelschema.model_validate ${FORMULA_NAME} ${role_name} || { log_err "Execution of model_validate ${_message} failed"; exit 1 ; }
       done
-    else
-      log_info "${SCHEMARDIR} not found!";
-    fi
+    done
+    for schema in $(find $SCHEMARDIR -mindepth 2 -type f -iname *.yaml); do
+        role_name=$(basename "${schema%*.yaml}")
+        os_release=$(echo $schema | rev | cut -d'/' -f2 | rev)
+        local _message="FORMULA:${FORMULA_NAME} ROLE:${role_name} against PILLAR:${role_name}"
+        log_info "model_validate ${_message}"
+        salt_run -m ${DEPSDIR}/salt-formula-salt --id=${os_release}_${role_name} modelschema.model_validate ${FORMULA_NAME} ${role_name} ${os_release} || { log_err "Execution of model_validate ${_message} failed"; exit 1 ; }
+    done
+  else
+    log_info "${SCHEMARDIR} not found!";
+  fi
 }
 
 dependency_check() {
@@ -243,6 +282,10 @@
 }
 
 ## Main
+
+log_info "Running version: ${__ScriptVersion}"
+log_info "Command line: '${__ScriptFullName} ${__ScriptArgs}'"
+
 trap _atexit INT TERM EXIT
 
 case $1 in
@@ -269,6 +312,6 @@
         prepare
 #        lint
         run
-#        run_model_validate
+        run_model_validate
         ;;
-esac
\ No newline at end of file
+esac
diff --git a/tests/test-requirements.txt b/tests/test-requirements.txt
new file mode 100644
index 0000000..bc7dc7f
--- /dev/null
+++ b/tests/test-requirements.txt
@@ -0,0 +1,4 @@
+jsonschema
+reno
+setuptools<45.0.0
+msgpack<1.0.0