Add support for JSON dashboards

This patch adds the support for JSON dashboards and also provides the
support for remote dashboards.
diff --git a/README.rst b/README.rst
index fcbdef8..f806ffe 100644
--- a/README.rst
+++ b/README.rst
@@ -44,7 +44,9 @@
           user: grafana
           password: passwd
 
-Server installed with default StackLight JSON dashboards
+Server installed with default StackLight JSON dashboards. This will
+be replaced by the possibility for a service to provide its own dashboard
+using salt-mine.
 
 .. code-block:: yaml
 
@@ -202,7 +204,7 @@
           span: 6
           editable: false
           type: graph
-          targets: 
+          targets:
           - refId: A
             target: "support_prd.cfg01_iot_tcpcloud_eu.cpu.0.idle"
           datasource: graphite01
diff --git a/_states/grafana3_dashboard.py b/_states/grafana3_dashboard.py
index 0087b2c..63090ab 100644
--- a/_states/grafana3_dashboard.py
+++ b/_states/grafana3_dashboard.py
@@ -65,6 +65,7 @@
             base_panels_from_pillar=None,
             base_rows_from_pillar=None,
             dashboard=None,
+            dashboard_format='yaml',
             profile='grafana'):
     '''
     Ensure the grafana dashboard exists and is managed.
@@ -84,19 +85,39 @@
     dashboard
         A dict that defines a dashboard that should be managed.
 
+    dashboard_format
+        You can use two formats for dashboards. You can use the JSON format
+        if you provide a complete dashboard in raw JSON or you can use the YAML
+        format (this is the default) and provide a description of the
+        dashboard in YAML.
+
     profile
         A pillar key or dict that contains grafana information
     '''
     ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}
-
-    base_dashboards_from_pillar = base_dashboards_from_pillar or []
-    base_panels_from_pillar = base_panels_from_pillar or []
-    base_rows_from_pillar = base_rows_from_pillar or []
     dashboard = dashboard or {}
 
     if isinstance(profile, six.string_types):
         profile = __salt__['config.option'](profile)
 
+    if dashboard_format == 'json':
+        # In this case, a raw JSON of the full dashboard is provided.
+        response = _update(dashboard, profile)
+
+        if response.get('status') == 'success':
+            ret['comment'] = 'Dashboard {0} created.'.format(name)
+            ret['changes']['new'] = 'Dashboard {0} created.'.format(name)
+        else:
+            ret['result'] = False
+            ret['comment'] = ("Failed to create dashboard {0}, "
+                              "response={1}").format(name, response)
+
+        return ret
+
+    base_dashboards_from_pillar = base_dashboards_from_pillar or []
+    base_panels_from_pillar = base_panels_from_pillar or []
+    base_rows_from_pillar = base_rows_from_pillar or []
+
     # Add pillar keys for default configuration
     base_dashboards_from_pillar = ([_DEFAULT_DASHBOARD_PILLAR] +
                                    base_dashboards_from_pillar)
@@ -439,18 +460,12 @@
     '''Delete a specific dashboard.'''
     request_url = "{0}/api/dashboards/{1}".format(profile.get('grafana_url'),
                                                   url)
-    if profile.get('grafana_token', False):
-        response = requests.delete(
-            request_url,
-            headers=_get_headers(profile),
-            timeout=profile.get('grafana_timeout'),
-        )
-    else:
-        response = requests.delete(
-            request_url,
-            auth=_get_auth(profile),
-            timeout=profile.get('grafana_timeout'),
-        )
+    response = requests.delete(
+        request_url,
+        auth=_get_auth(profile),
+        headers=_get_headers(profile),
+        timeout=profile.get('grafana_timeout'),
+    )
     data = response.json()
     return data
 
@@ -461,30 +476,28 @@
         'dashboard': dashboard,
         'overwrite': True
     }
-    request_url = "{0}/api/dashboards/db".format(profile.get('grafana_url'))
-    if profile.get('grafana_token', False):
-        response = requests.post(
-            request_url,
-            headers=_get_headers(profile),
-            json=payload
-        )
-    else:
-        response = requests.post(
-            request_url,
-            auth=_get_auth(profile),
-            json=payload
-        )
+    response = requests.post(
+        "{0}/api/dashboards/db".format(profile.get('grafana_url')),
+        auth=_get_auth(profile),
+        headers=_get_headers(profile),
+        json=payload
+    )
     return response.json()
 
 
 def _get_headers(profile):
-    return {
-        'Accept': 'application/json',
-        'Authorization': 'Bearer {0}'.format(profile['grafana_token'])
-    }
+    headers = {'Content-type': 'application/json'}
+
+    if profile.get('grafana_token', False):
+        headers['Authorization'] = 'Bearer {0}'.format(profile['grafana_token'])
+
+    return headers
 
 
 def _get_auth(profile):
+    if profile.get('grafana_token', False):
+        return None
+
     return requests.auth.HTTPBasicAuth(
         profile['grafana_user'],
         profile['grafana_password']
@@ -568,4 +581,4 @@
     for k, v in six.iteritems(d):
         if v:
             ret[k] = v
-    return ret
\ No newline at end of file
+    return ret
diff --git a/grafana/client.sls b/grafana/client.sls
index 47cef61..1275f1b 100644
--- a/grafana/client.sls
+++ b/grafana/client.sls
@@ -36,52 +36,56 @@
 
 {%- if client.remote_data.engine == 'salt_mine' %}
 {%- for node_name, node_grains in salt['mine.get']('*', 'grains.items').iteritems() %}
-{%- if node_grains.grafana is defined %}
-{%- set raw_dict = salt['grains.filter_by']({'default': raw_dict}, merge=node_grains.grafana.get('dashboard', {})) %}
-{%- endif %}
+  {%- if node_grains.grafana is defined %}
+  {%- set raw_dict = salt['grains.filter_by']({'default': raw_dict}, merge=node_grains.grafana.get('dashboard', {})) %}
+  {%- endif %}
 {%- endfor %}
 {%- endif %}
 
 {%- if client.dashboard is defined %}
-{%- set raw_dict = salt['grains.filter_by']({'default': raw_dict}, merge=client.dashboard) %}
+  {%- set raw_dict = salt['grains.filter_by']({'default': raw_dict}, merge=client.dashboard) %}
 {%- endif %}
 
 {%- for dashboard_name, dashboard in raw_dict.iteritems() %}
-{%- set rows = [] %}
-{%- for row_name, row in dashboard.get('row', {}).iteritems() %}
-{%- set panels = [] %}
-{%- for panel_name, panel in row.get('panel', {}).iteritems() %}
-{%- set targets = [] %}
-{%- for target_name, target in panel.get('target', {}).iteritems() %}
-{%- do targets.extend([target]) %}
-{%- endfor %}
-{%- do panel.update({'targets': targets}) %}
-{%- do panels.extend([panel]) %}
-{%- endfor %}
-{%- do row.update({'panels': panels}) %}
-{%- do rows.extend([row]) %}
-{%- endfor %}
-{%- do dashboard.update({'rows': rows}) %}
-{%- do final_dict.update({dashboard_name: dashboard}) %}
+  {%- if dashboard.get('format', 'yaml')|lower == 'yaml' %}
+  # Dashboards in JSON format are considered as blob
+  {%- set rows = [] %}
+  {%- for row_name, row in dashboard.get('row', {}).iteritems() %}
+    {%- set panels = [] %}
+    {%- for panel_name, panel in row.get('panel', {}).iteritems() %}
+      {%- set targets = [] %}
+      {%- for target_name, target in panel.get('target', {}).iteritems() %}
+        {%- do targets.extend([target]) %}
+      {%- endfor %}
+      {%- do panel.update({'targets': targets}) %}
+      {%- do panels.extend([panel]) %}
+    {%- endfor %}
+    {%- do row.update({'panels': panels}) %}
+    {%- do rows.extend([row]) %}
+  {%- endfor %}
+  {%- do dashboard.update({'rows': rows}) %}
+  {%- endif %}
+
+  {%- do final_dict.update({dashboard_name: dashboard}) %}
 {%- endfor %}
 
 {%- for dashboard_name, dashboard in final_dict.iteritems() %}
-
-{%- if dashboard.get('enabled', True) %}
-
+  {%- if dashboard.get('enabled', True) %}
 grafana_client_dashboard_{{ dashboard_name }}:
   grafana3_dashboard.present:
   - name: {{ dashboard_name }}
+    {%- if dashboard.get('format', 'yaml')|lower == 'json' %}
+    {%- import_json dashboard.template as dash %}
+  - dashboard: {{ dash|json }}
+  - dashboard_format: json
+    {%- else %}
   - dashboard: {{ dashboard }}
-
-{%- else %}
-
+    {%- endif %}
+  {%- else %}
 grafana_client_dashboard_{{ dashboard_name }}:
   grafana3_dashboard.absent:
   - name: {{ dashboard_name }}
-
-{%- endif %}
-
+  {%- endif %}
 {%- endfor %}
 
 {%- endif %}
diff --git a/grafana/collector.sls b/grafana/collector.sls
index b1f76b1..4532547 100644
--- a/grafana/collector.sls
+++ b/grafana/collector.sls
@@ -13,6 +13,7 @@
 {# Loading the other service support metadata for localhost #}
 
 {%- for service_name, service in pillar.iteritems() %}
+{%- if service.get('_support', {}).get('grafana', {}).get('enabled', False) %}
 
 {%- macro load_grains_file(grains_fragment_file) %}{% include grains_fragment_file ignore missing %}{% endmacro %}
 
@@ -20,6 +21,7 @@
 {%- set grains_yaml = load_grains_file(grains_fragment_file)|load_yaml %}
 {%- set service_grains = salt['grains.filter_by']({'default': service_grains}, merge=grains_yaml) %}
 
+{%- endif %}
 {%- endfor %}
 
 grafana_grain:
diff --git a/metadata/service/collector.yml b/metadata/service/collector.yml
new file mode 100644
index 0000000..f788dd0
--- /dev/null
+++ b/metadata/service/collector.yml
@@ -0,0 +1,6 @@
+applications:
+- grafana
+parameters:
+  grafana:
+    collector:
+      enabled: true