Support dimensions in gse metrics
diff --git a/_modules/heka_alarming.py b/_modules/heka_alarming.py
index fe5b3b4..d87ed73 100644
--- a/_modules/heka_alarming.py
+++ b/_modules/heka_alarming.py
@@ -46,14 +46,14 @@
     return ' && '.join(matchers)
 
 
-def dimensions(alarm):
+def dimensions(alarm_or_alarm_cluster):
     """
     Return a dict alarm dimensions. Each dimension is validated, and an
     Exception is raised if a dimension is invalid.
 
     Valid characters are a-z, 0-9, _, - and /.
     """
-    dimensions = alarm.get('dimension', {})
+    dimensions = alarm_or_alarm_cluster.get('dimension', {})
     for name, value in dimensions.items():
         if name in _disallowed_dimensions:
             raise Exception(
diff --git a/heka/files/lua/common/gse.lua b/heka/files/lua/common/gse.lua
index 2e86695..d1a01a6 100644
--- a/heka/files/lua/common/gse.lua
+++ b/heka/files/lua/common/gse.lua
@@ -125,7 +125,7 @@
 
 -- compute the cluster metric and inject it into the Heka pipeline
 -- the metric's value is computed using the status of its members
-function inject_cluster_metric(cluster_name, to_alerting)
+function inject_cluster_metric(cluster_name, dimensions, to_alerting)
     local payload
     local status, alarms = resolve_status(cluster_name)
 
@@ -156,6 +156,11 @@
         }
     }
 
+    for name, value in pairs(dimensions) do
+        table.insert(msg.Fields.tag_fields, name)
+        msg.Fields[name] = value
+    end
+
     lma.inject_tags(msg)
     lma.safe_inject_message(msg)
 end
diff --git a/heka/files/lua/filters/gse_cluster_filter.lua b/heka/files/lua/filters/gse_cluster_filter.lua
index 58f5b62..e8a83f5 100644
--- a/heka/files/lua/filters/gse_cluster_filter.lua
+++ b/heka/files/lua/filters/gse_cluster_filter.lua
@@ -24,6 +24,7 @@
 local interval_in_ns = interval * 1e9
 local max_inject = (read_config('max_inject') or 10) + 0
 local warm_up_period = ((read_config('warm_up_period') or 0) + 0) * 1e9
+local dimensions_json = read_config('dimensions') or ''
 local activate_alerting = read_config('activate_alerting') or true
 
 local is_active = false
@@ -42,6 +43,11 @@
         attributes.group_by, policy)
 end
 
+local ok, dimensions = pcall(cjson.decode, dimensions_json)
+if not ok then
+    error(string.format('dimensions JSON is invalid (%s)', dimensions_json))
+end
+
 function process_message()
     local name = read_message('Fields[name]')
     local hostname = read_message('Fields[hostname]')
@@ -101,7 +107,7 @@
     local injected = 0
     for i, cluster_name in ipairs(gse.get_ordered_clusters()) do
         if last_index == nil or i > last_index then
-            gse.inject_cluster_metric(cluster_name, activate_alerting)
+            gse.inject_cluster_metric(cluster_name, dimensions, activate_alerting)
             last_index = i
             injected = injected + 1
 
diff --git a/heka/files/toml/filter/gse_alarm_cluster.toml b/heka/files/toml/filter/gse_alarm_cluster.toml
index 375674a..72b1923 100644
--- a/heka/files/toml/filter/gse_alarm_cluster.toml
+++ b/heka/files/toml/filter/gse_alarm_cluster.toml
@@ -8,6 +8,7 @@
 
 [gse_{{ alarm_cluster_name }}_filter.config]
 topology_file = "gse_{{ alarm_cluster_name|replace('-', '_') }}_topology"
+dimensions = '{{ salt['heka_alarming.dimensions'](alarm_cluster)|json }}'
 activate_alerting = {{ alarm_cluster.alerting|default(True)|lower }}
 {%- if alarm_cluster.interval is defined %}
 interval = {{ alarm_cluster.interval }}
diff --git a/tests/lua/test_gse.lua b/tests/lua/test_gse.lua
index 899e15e..be892f5 100644
--- a/tests/lua/test_gse.lua
+++ b/tests/lua/test_gse.lua
@@ -184,33 +184,36 @@
     end
 
     function TestGse:test_inject_cluster_metric_for_nova()
-        gse.inject_cluster_metric('nova', true)
+        gse.inject_cluster_metric('nova', {key = "val"}, true)
         local metric = last_injected_msg
         assertEquals(metric.Type, 'gse_metric')
         assertEquals(metric.Fields.member, 'nova')
         assertEquals(metric.Fields.name, 'cluster_status')
         assertEquals(metric.Fields.value, consts.OKAY)
+        assertEquals(metric.Fields.key, 'val')
         assertEquals(metric.Payload, '{"alarms":[]}')
     end
 
     function TestGse:test_inject_cluster_metric_for_glance()
-        gse.inject_cluster_metric('glance', true)
+        gse.inject_cluster_metric('glance', {key = "val"}, true)
         local metric = last_injected_msg
         assertEquals(metric.Type, 'gse_metric')
         assertEquals(metric.Fields.member, 'glance')
         assertEquals(metric.Fields.name, 'cluster_status')
         assertEquals(metric.Fields.value, consts.DOWN)
+        assertEquals(metric.Fields.key, 'val')
         assert(metric.Payload:match("glance%-registry endpoints are down"))
         assert(metric.Payload:match("glance%-api endpoint is down on node%-1"))
     end
 
     function TestGse:test_inject_cluster_metric_for_heat()
-        gse.inject_cluster_metric('heat', true)
+        gse.inject_cluster_metric('heat', {key = "val"}, true)
         local metric = last_injected_msg
         assertEquals(metric.Type, 'gse_metric')
         assertEquals(metric.Fields.member, 'heat')
         assertEquals(metric.Fields.name, 'cluster_status')
         assertEquals(metric.Fields.value, consts.WARN)
+        assertEquals(metric.Fields.key, 'val')
         assert(metric.Payload:match("5xx errors detected"))
         assert(metric.Payload:match("1 RabbitMQ node out of 3 is down"))
     end