Merge "Fix property validation for TemplateResource during update"
diff --git a/common/clients.py b/common/clients.py
index 1ba3a21..6042456 100644
--- a/common/clients.py
+++ b/common/clients.py
@@ -12,6 +12,7 @@
 
 import os
 
+import ceilometerclient.client
 import cinderclient.client
 import heatclient.client
 import keystoneclient.exceptions
@@ -30,6 +31,7 @@
     CINDERCLIENT_VERSION = '1'
     HEATCLIENT_VERSION = '1'
     NOVACLIENT_VERSION = '2'
+    CEILOMETER_VERSION = '2'
 
     def __init__(self, conf):
         self.conf = conf
@@ -39,6 +41,7 @@
         self.network_client = self._get_network_client()
         self.volume_client = self._get_volume_client()
         self.object_client = self._get_object_client()
+        self.metering_client = self._get_metering_client()
 
     def _get_orchestration_client(self):
         region = self.conf.region
@@ -136,3 +139,27 @@
             'insecure': dscv,
         }
         return swiftclient.client.Connection(**args)
+
+    def _get_metering_client(self):
+        dscv = self.conf.disable_ssl_certificate_validation
+
+        keystone = self._get_identity_client()
+        endpoint = keystone.service_catalog.url_for(
+            attr='region',
+            filter_value=self.conf.region,
+            service_type='metering',
+            endpoint_type='publicURL')
+
+        args = {
+            'username': self.conf.username,
+            'password': self.conf.password,
+            'tenant_name': self.conf.tenant_name,
+            'auth_url': self.conf.auth_url,
+            'insecure': dscv,
+            'region_name': self.conf.region,
+            'endpoint_type': 'publicURL',
+            'service_type': 'metering',
+        }
+
+        return ceilometerclient.client.Client(self.CEILOMETER_VERSION,
+                                              endpoint, **args)
diff --git a/common/test.py b/common/test.py
index 7c8300d..4168fb4 100644
--- a/common/test.py
+++ b/common/test.py
@@ -87,6 +87,7 @@
         self.network_client = self.manager.network_client
         self.volume_client = self.manager.volume_client
         self.object_client = self.manager.object_client
+        self.metering_client = self.manager.metering_client
         self.useFixture(fixtures.FakeLogger(format=_LOG_FORMAT))
         self.updated_time = {}
 
diff --git a/scenario/templates/test_ceilometer_alarm.yaml b/scenario/templates/test_ceilometer_alarm.yaml
new file mode 100644
index 0000000..01bc790
--- /dev/null
+++ b/scenario/templates/test_ceilometer_alarm.yaml
@@ -0,0 +1,33 @@
+heat_template_version: 2013-05-23
+resources:
+  asg:
+    type: OS::Heat::AutoScalingGroup
+    properties:
+      max_size: 5
+      min_size: 1
+      resource:
+        type: OS::Heat::RandomString
+  scaleup_policy:
+    type: OS::Heat::ScalingPolicy
+    properties:
+      adjustment_type: change_in_capacity
+      auto_scaling_group_id: {get_resource: asg}
+      cooldown: 0
+      scaling_adjustment: 1
+  alarm:
+    type: OS::Ceilometer::Alarm
+    properties:
+      description: Scale-up if the average CPU > 50% for 1 minute
+      meter_name: test_meter
+      statistic: count
+      comparison_operator: ge
+      threshold: 1
+      period: 60
+      evaluation_periods: 1
+      alarm_actions:
+        - {get_attr: [scaleup_policy, alarm_url]}
+      matching_metadata:
+        metadata.metering.stack_id: {get_param: "OS::stack_id"}
+outputs:
+  asg_size:
+    value: {get_attr: [asg, current_size]}
diff --git a/scenario/test_ceilometer_alarm.py b/scenario/test_ceilometer_alarm.py
new file mode 100644
index 0000000..d8f21fd
--- /dev/null
+++ b/scenario/test_ceilometer_alarm.py
@@ -0,0 +1,57 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_log import log as logging
+
+from heat_integrationtests.common import test
+
+LOG = logging.getLogger(__name__)
+
+
+class CeilometerAlarmTest(test.HeatIntegrationTest):
+    """Class is responsible for testing of ceilometer usage."""
+    def setUp(self):
+        super(CeilometerAlarmTest, self).setUp()
+        self.client = self.orchestration_client
+        self.template = self._load_template(__file__,
+                                            'test_ceilometer_alarm.yaml',
+                                            'templates')
+
+    def check_instance_count(self, stack_identifier, expected):
+        stack = self.client.stacks.get(stack_identifier)
+        actual = self._stack_output(stack, 'asg_size')
+        if actual != expected:
+            LOG.warn('check_instance_count exp:%d, act:%s' % (expected,
+                                                              actual))
+        return actual == expected
+
+    def test_alarm(self):
+        """Confirm we can create an alarm and trigger it."""
+
+        # 1. create the stack
+        stack_identifier = self.stack_create(template=self.template)
+
+        # 2. send ceilometer a metric (should cause the alarm to fire)
+        sample = {}
+        sample['counter_type'] = 'gauge'
+        sample['counter_name'] = 'test_meter'
+        sample['counter_volume'] = 1
+        sample['counter_unit'] = 'count'
+        sample['resource_metadata'] = {'metering.stack_id':
+                                       stack_identifier.split('/')[-1]}
+        sample['resource_id'] = 'shouldnt_matter'
+        self.metering_client.samples.create(**sample)
+
+        # 3. confirm we get a scaleup.
+        # Note: there is little point waiting more than 60s+time to scale up.
+        self.assertTrue(test.call_until_true(
+            120, 2, self.check_instance_count, stack_identifier, 2))