Merge changes from topic '3939'

* changes:
  Add support of Keystone OIDC Federation
  Rename  websso to federation
diff --git a/README.rst b/README.rst
index a7f7082..1e0e822 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..2155b12
--- /dev/null
+++ b/_grains/keystone_policy.py
@@ -0,0 +1,15 @@
+#!/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)
+    if keystone_policy_mod:
+        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/files/keystone.conf b/keystone/files/keystone.conf
deleted file mode 100644
index e6c9de2..0000000
--- a/keystone/files/keystone.conf
+++ /dev/null
@@ -1,31 +0,0 @@
-{%- from "keystone/map.jinja" import client with context %}
-{%- for profile_name, identity in client.server.iteritems() %}
-
-{%- if identity.admin.get('protocol', 'http') == 'http' %}
-{%- set protocol = 'http' %}
-{%- else %}
-{%- set protocol = 'https' %}
-{%- endif %}
-
-{%- if identity.admin.get('api_version', '2') == '3' %}
-{%- set version = "v3" %}
-{%- else %}
-{%- set version = "v2.0" %}
-{%- endif %}
-
-{%- if identity.admin.user is defined %}
-
-{%- if identity.admin.token is not defined %}
-
-{{ profile_name }}:
-  keystone.user: '{{ identity.admin.user }}'
-  keystone.password: '{{ identity.admin.password }}'
-  keystone.tenant: '{{ identity.admin.project }}'
-  keystone.auth_url: '{{ protocol+'://'+identity.admin.host+':'+identity.admin.port|string+'/'+version }}'
-  keystone.region_name: '{{ identity.admin.region_name }}'
-
-{%- endif %}
-
-{%- endif %}
-
-{%- endfor %}
diff --git a/keystone/meta/salt.yml b/keystone/meta/salt.yml
index 8961ec1..ccd9d91 100644
--- a/keystone/meta/salt.yml
+++ b/keystone/meta/salt.yml
@@ -6,21 +6,57 @@
     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) }}
 
 minion:
-  {% if pillar.keystone.server is defined or pillar.keystone.client is defined %}
+  {%- if pillar.get('keystone', {}).get('server') or pillar.get('keystone', {}).get('client') %}
+    {%- from "keystone/map.jinja" import server with context %}
+    {%- from "keystone/map.jinja" import client with context %}
+
   keystone:
-    {%- if pillar.keystone.get('server', {'enabled': False}).enabled -%}
-    {%- from "keystone/map.jinja" import server with context -%}
+    {%- if pillar.keystone.get('server', {'enabled': False}).enabled %}
     keystone.token: '{{ server.service_token }}'
     keystone.endpoint: 'http://{{ server.bind.address }}:{{ server.bind.private_port }}/v2.0'
-    {%- else -%}
-    {%- from "keystone/map.jinja" import client with context -%}
+    {%- else %}
+      {%- if client.get('server', {}).get('user') %}
     keystone.user: '{{ client.server.user }}'
     keystone.password: '{{ client.server.password }}'
     keystone.tenant: '{{ client.server.tenant }}'
     keystone.auth_url: 'http://{{ client.server.host }}:{{ client.server.public_port }}/v2.0/'
+      {%- endif %}
     {%- endif %}
+
+    {#- Profile based metadata #}
+    {%- for profile_name, identity in client.get('server', {}).iteritems() %}
+      {%- if identity.admin.get('protocol', 'http') == 'http' %}
+        {%- set protocol = 'http' %}
+      {%- else %}
+        {%- set protocol = 'https' %}
+      {%- endif %}
+
+      {%- if identity.admin.get('api_version', '2') == '3' %}
+        {%- set version = "v3" %}
+      {%- else %}
+        {%- set version = "v2.0" %}
+      {%- endif %}
+
+      {%- if identity.admin.user is defined %}
+        {%- if identity.admin.token is not defined %}
+
+    {{ profile_name }}:
+      keystone.user: '{{ identity.admin.user }}'
+      keystone.password: '{{ identity.admin.password }}'
+      keystone.tenant: '{{ identity.admin.project }}'
+      keystone.auth_url: '{{ protocol+'://'+identity.admin.host+':'+identity.admin.port|string+'/'+version }}'
+      keystone.region_name: '{{ identity.admin.region_name }}'
+
+        {%- endif %}
+      {%- endif %}
+    {%- endfor %}
   {%- endif %}
 
 {#-
diff --git a/keystone/server.sls b/keystone/server.sls
index 05d10c0..b4de945 100644
--- a/keystone/server.sls
+++ b/keystone/server.sls
@@ -90,11 +90,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) %}
@@ -102,6 +106,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:
diff --git a/metadata/service/support.yml b/metadata/service/support.yml
index 283ca7e..df136a7 100644
--- a/metadata/service/support.yml
+++ b/metadata/service/support.yml
@@ -6,7 +6,7 @@
       heka:
         enabled: true
       sensu:
-        enabled: true
+        enabled: false
       sphinx:
         enabled: true
       config: