Keystone policy module, states and grain
diff --git a/README.rst b/README.rst
index 257037d..6f9326e 100644
--- a/README.rst
+++ b/README.rst
@@ -86,6 +86,17 @@
             admin_address: 10.0.0.20
             admin_port: 8774
 
+Keystone with custom policies. Keys with specified rules are created or set to this value if they already exists. Keys with no value (like our "existing_rule") are deleted from the policy file.
+
+.. code-block:: yaml
+
+    keystone:
+      server:
+        enabled: true
+        policy:
+          new_rule: "rule:admin_required"
+          existing_rule:
+
 Keystone memcached storage for tokens
 
 .. code-block:: yaml
diff --git a/_grains/keystone_policy.py b/_grains/keystone_policy.py
new file mode 100644
index 0000000..1812c03
--- /dev/null
+++ b/_grains/keystone_policy.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+import salt.config
+import salt.loader
+
+
+def main():
+    path = "/etc/keystone/policy.json"
+    __opts__ = salt.config.minion_config('/etc/salt/minion')
+    keystone_policy_mod = salt.loader.raw_mod(__opts__, 'keystone_policy', None)
+    result = keystone_policy_mod['keystone_policy.rule_list'](path)
+    if result and 'Error' not in result:
+        return {'keystone_policy': result}
+    return {}
+
diff --git a/_modules/keystone_policy.py b/_modules/keystone_policy.py
new file mode 100644
index 0000000..05b9215
--- /dev/null
+++ b/_modules/keystone_policy.py
@@ -0,0 +1,68 @@
+import io
+import json
+import logging
+
+LOG = logging.getLogger(__name__)
+
+
+def __virtual__():
+    return True
+
+
+def rule_list(path, **kwargs):
+    try:
+        with io.open(path, 'r') as file_handle:
+            rules = json.loads(file_handle.read())
+        rules =  {str(k): str(v) for (k, v) in rules.items()}
+    except Exception as e:
+        msg = "Unable to load policy JSON %s: %s" % (path, repr(e))
+        LOG.debug(msg)
+        rules = {'Error': msg}
+    return rules
+
+
+def rule_delete(name, path, **kwargs):
+    ret = {}
+    rules = __salt__['keystone_policy.rule_list'](path, **kwargs)
+    if 'Error' not in rules:
+        if name not in rules:
+            return ret
+        del rules[name]
+        try:
+            with io.open(path, 'w') as file_handle:
+                file_handle.write(unicode(json.dumps(rules, indent=4)))
+        except Exception as e:
+            msg = "Unable to save policy json: %s" % repr(e)
+            LOG.error(msg)
+            return {'Error': msg}
+        ret = 'Rule {0} deleted'.format(name)
+    return ret
+
+
+def rule_set(name, rule, path, **kwargs):
+    rules = __salt__['keystone_policy.rule_list'](path, **kwargs)
+    if 'Error' not in rules:
+        if name in rules and rules[name] == rule:
+            return {name: 'Rule %s already exists and is in correct state' % name}
+        rules.update({name: rule})
+        try:
+            with io.open(path, 'w') as file_handle:
+                file_handle.write(unicode(json.dumps(rules, indent=4)))
+        except Exception as e:
+            msg = "Unable to save policy JSON %s: %s" % (path, repr(e))
+            LOG.error(msg)
+            return {'Error': msg}
+        return rule_get(name, path, **kwargs)
+    return rules
+
+
+def rule_get(name, path, **kwargs):
+    ret = {}
+    rules = __salt__['keystone_policy.rule_list'](path, **kwargs)
+    if 'Error' in rules:
+        ret['Error'] = rules['Error']
+    elif name in rules:
+        ret[name] = rules.get(name)
+
+    return ret
+
diff --git a/_states/keystone_policy.py b/_states/keystone_policy.py
index 2d34e06..e7a4a6a 100644
--- a/_states/keystone_policy.py
+++ b/_states/keystone_policy.py
@@ -8,46 +8,74 @@
 
 .. code-block:: yaml
 
-/etc/keystone/policy.json:
-  keystone_policy.present:
-    - override_data:
-        override_key: override_value
-    - formatter: json
+  my_rule_present:
+    keystone_policy.rule_present:
+      - name: rule_name
+      - rule: rule
+      - path: /etc/keystone/policy.json
+
+  my_rule_absent:
+    keystone_policy.rule_absent:
+      - name: rule_name
+      - path: /etc/keystone/policy.json
 
 '''
 import logging
-import json
 
 log = logging.getLogger(__name__)
 
-JSON_LOCATION = '/etc/keystone/policy.json'
+
+def __virtual__():
+    return True
 
 
-def _deep_merge(dct, merge_dct):
-    for k, v in merge_dct.iteritems():
-        if (k in dct and isinstance(dct[k], dict)):
-            _deep_merge(dct[k], merge_dct[k])
-        else:
-            dct[k] = merge_dct[k]
-
-
-def present(name, override_data={}, **kwargs):
+def rule_present(name, rule, path, **kwargs):
     '''
-    Ensures that given key present in policy.json file. This is a wrapper
-    around file.serialize state with additional argument: override_data.
-    Rest parameters of file.serialize can be safely used as well.
-    Function reads contents of existing policy.json file into a python
-    dictionary. User defined data populated to this dictionary using deep
-    merge procedure.
-
-    :param name:          Name of the resource
-    :param override_data: User defined data with overrides
+    Ensures that the policy rule exists
+    
+    :param name: Rule name
+    :param rule: Rule
+    :param path: Path to policy file
     '''
-    with open(JSON_LOCATION) as policy_json:
-        json_content = json.load(policy_json)
-
-    _deep_merge(json_content, override_data)
-
-    kwargs['dataset'] = json_content
-    ret = __states__['file.serialize']('/etc/keystone/policy.json', **kwargs)
+    rule = rule or ""
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'Rule "{0}" already exists and is in correct state'.format(name)}
+    rule_check = __salt__['keystone_policy.rule_get'](name, path, **kwargs)
+    if not rule_check:
+        __salt__['keystone_policy.rule_set'](name, rule, path, **kwargs)
+        ret['comment'] = 'Rule {0} has been created'.format(name)
+        ret['changes']['Rule'] = 'Rule %s: "%s" has been created' % (name, rule)
+    elif 'Error' in rule_check:
+        ret['comment'] = rule_check.get('Error')
+        ret['result'] = False
+    elif rule_check[name] != rule:
+        __salt__['keystone_policy.rule_set'](name, rule, path, **kwargs)
+        ret['comment'] = 'Rule %s has been changed' % (name,)
+        ret['changes']['Old Rule'] = '%s: "%s"' % (name, rule_check[name])
+        ret['changes']['New Rule'] = '%s: "%s"' % (name, rule)
     return ret
+
+
+def rule_absent(name, path, **kwargs):
+    '''
+    Ensures that the policy rule does not exist
+
+    :param name: Rule name
+    :param path: Path to policy file
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'Rule "{0}" is already absent'.format(name)}
+    rule_check = __salt__['keystone_policy.rule_get'](name, path, **kwargs)
+    if rule_check:
+        __salt__['keystone_policy.rule_delete'](name, path, **kwargs)
+        ret['comment'] = 'Rule {0} has been deleted'.format(name)
+        ret['changes']['Rule'] = 'Rule %s: "%s" has been deleted' % (name, rule_check[name])
+    elif 'Error' in rule_check:
+        ret['comment'] = rule_check.get('Error')
+        ret['result'] = False
+    return ret
+
diff --git a/keystone/meta/salt.yml b/keystone/meta/salt.yml
index c5f5e3a..d23f40b 100644
--- a/keystone/meta/salt.yml
+++ b/keystone/meta/salt.yml
@@ -6,4 +6,9 @@
     priority: 510
   control:
     priority: 520
+grain:
+  keystone:
+    keystone_policy:
+      {%- set policy_grains = salt['keystone_policy.rule_list'](path="/etc/keystone/policy.json") %}
+      {{ policy_grains|yaml(False)|indent(6) }}
 
diff --git a/keystone/server.sls b/keystone/server.sls
index 384911f..08251cd 100644
--- a/keystone/server.sls
+++ b/keystone/server.sls
@@ -97,11 +97,15 @@
     - service: keystone_service
   {%- endif %}
 
-/etc/keystone/policy.json:
-  keystone_policy.present:
-  - override_data:
-      {{ server.get('policy', {})|yaml }}
-  - formatter: json
+{%- for name, rule in server.get('policy', {}).iteritems() %}
+
+{%- if rule != None %}
+
+rule_{{ name }}_present:
+  keystone_policy.rule_present:
+  - path: /etc/keystone/policy.json
+  - name: {{ name }}
+  - rule: {{ rule }}
   - require:
     - pkg: keystone_packages
   {%- if not grains.get('noservices', False) %}
@@ -109,6 +113,23 @@
     - service: keystone_service
   {%- endif %}
 
+{%- else %}
+
+rule_{{ name }}_absent:
+  keystone_policy.rule_absent:
+  - path: /etc/keystone/policy.json
+  - name: {{ name }}
+  - require:
+    - pkg: keystone_packages
+  {%- if not grains.get('noservices', False) %}
+  - watch_in:
+    - service: keystone_service
+  {%- endif %}
+
+{%- endif %}
+
+{%- endfor %}
+
 {%- if server.get("domain", {}) %}
 
 /etc/keystone/domains: