~ubuntu-branches/ubuntu/saucy/heat/saucy

« back to all changes in this revision

Viewing changes to heat/tests/functional/test_CFN_API_Actions.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Yolanda Robla, Chuck Short
  • Date: 2013-07-22 16:22:29 UTC
  • mfrom: (1.1.2)
  • Revision ID: package-import@ubuntu.com-20130722162229-zzvfu40id94ii0hc
Tags: 2013.2~b2-0ubuntu1
[ Yolanda Robla ]
* debian/tests: added autopkg tests

[ Chuck Short ]
* New upstream release
* debian/control:
  - Add python-pbr to build-depends.
  - Add python-d2to to build-depends.
  - Dropped python-argparse.
  - Add python-six to build-depends.
  - Dropped python-sendfile.
  - Dropped python-nose.
  - Added testrepository.
  - Added python-testtools.
* debian/rules: Run testrepository instead of nosetets.
* debian/patches/removes-lxml-version-limitation-from-pip-requires.patch: Dropped
  no longer needed.
* debian/patches/fix-package-version-detection-when-building-doc.patch: Dropped
  no longer needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
 
#
3
 
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
4
 
#    not use this file except in compliance with the License. You may obtain
5
 
#    a copy of the License at
6
 
#
7
 
#         http://www.apache.org/licenses/LICENSE-2.0
8
 
#
9
 
#    Unless required by applicable law or agreed to in writing, software
10
 
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
 
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
 
#    License for the specific language governing permissions and limitations
13
 
#
14
 
 
15
 
import os
16
 
import util
17
 
import verify
18
 
import re
19
 
from nose.plugins.attrib import attr
20
 
import unittest
21
 
import json
22
 
 
23
 
from heat.common import template_format
24
 
 
25
 
 
26
 
@attr(speed='slow')
27
 
@attr(tag=['func', 'wordpress', 'api', 'cfn', 'F17'])
28
 
class CfnApiFunctionalTest(unittest.TestCase):
29
 
    '''
30
 
    This test launches a wordpress stack then attempts to verify
31
 
    correct operation of all actions supported by the heat CFN API
32
 
 
33
 
    Note we use class-level fixtures to avoid setting up a new stack
34
 
    for every test method, we set up the stack once then do all the
35
 
    tests, this means all tests methods are performed on one class
36
 
    instance, instead of creating a new class for every method, which
37
 
    is the normal nose unittest.TestCase behavior.
38
 
 
39
 
    The nose docs are a bit vague on how to do this, but it seems that
40
 
    (setup|teardown)All works and they have to be classmethods.
41
 
 
42
 
    Contrary to the nose docs, the class can be a unittest.TestCase subclass
43
 
    '''
44
 
    @classmethod
45
 
    def setupAll(cls):
46
 
        print "SETUPALL"
47
 
        template = 'WordPress_Single_Instance.template'
48
 
 
49
 
        stack_paramstr = ';'.join(['InstanceType=m1.xlarge',
50
 
                         'DBUsername=dbuser',
51
 
                         'DBPassword=' + os.environ['OS_PASSWORD']])
52
 
 
53
 
        cls.logical_resource_name = 'WikiDatabase'
54
 
        cls.logical_resource_type = 'AWS::EC2::Instance'
55
 
 
56
 
        # Just to get the assert*() methods
57
 
        class CfnApiFunctions(unittest.TestCase):
58
 
            @unittest.skip('Not a real test case')
59
 
            def runTest(self):
60
 
                pass
61
 
 
62
 
        inst = CfnApiFunctions()
63
 
        cls.stack = util.Stack(inst, template, 'F17', 'x86_64', 'cfntools',
64
 
                               stack_paramstr)
65
 
        cls.WikiDatabase = util.Instance(inst, cls.logical_resource_name)
66
 
 
67
 
        try:
68
 
            cls.stack.create()
69
 
            cls.WikiDatabase.wait_for_boot()
70
 
            cls.WikiDatabase.check_cfntools()
71
 
            cls.WikiDatabase.wait_for_provisioning()
72
 
 
73
 
            cls.logical_resource_status = "CREATE_COMPLETE"
74
 
 
75
 
            # Save some compiled regexes and strings for response validation
76
 
            cls.time_re = re.compile(
77
 
                "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$")
78
 
            cls.description_re = re.compile(
79
 
                "^AWS CloudFormation Sample Template")
80
 
            cls.stack_status = "CREATE_COMPLETE"
81
 
            cls.stack_status_reason = "Stack successfully created"
82
 
            cls.stack_timeout = str(60)
83
 
            cls.stack_disable_rollback = "True"
84
 
 
85
 
            # Match the expected format for an instance's physical resource ID
86
 
            cls.phys_res_id_re = re.compile(
87
 
                "^[0-9a-z]*-[0-9a-z]*-[0-9a-z]*-[0-9a-z]*-[0-9a-z]*$")
88
 
        except Exception as ex:
89
 
            print "setupAll failed : %s" % ex
90
 
            cls.stack.cleanup()
91
 
            raise
92
 
 
93
 
    @classmethod
94
 
    def teardownAll(cls):
95
 
        print "TEARDOWNALL"
96
 
        cls.stack.cleanup()
97
 
 
98
 
    def test_instance(self):
99
 
        # ensure wordpress was installed by checking for expected
100
 
        # configuration file over ssh
101
 
        # This is the same as the standard wordress template test
102
 
        # but we still do it to prove the stack is OK
103
 
        self.assertTrue(self.WikiDatabase.file_present
104
 
                        ('/etc/wordpress/wp-config.php'))
105
 
        print "Wordpress installation detected"
106
 
 
107
 
        # Verify the output URL parses as expected, ie check that
108
 
        # the wordpress installation is operational
109
 
        stack_url = self.stack.get_stack_output("WebsiteURL")
110
 
        print "Got stack output WebsiteURL=%s, verifying" % stack_url
111
 
        ver = verify.VerifyStack()
112
 
        self.assertTrue(ver.verify_wordpress(stack_url))
113
 
 
114
 
    def testListStacks(self):
115
 
        response = self.stack.heatclient.list_stacks()
116
 
        prefix = '/ListStacksResponse/ListStacksResult/StackSummaries/member'
117
 
 
118
 
        stack_id = self.stack.response_xml_item(response, prefix, "StackId")
119
 
        self.stack.check_stackid(stack_id)
120
 
 
121
 
        update_time = self.stack.response_xml_item(response, prefix,
122
 
                                                   "LastUpdatedTime")
123
 
        self.assertTrue(self.time_re.match(update_time) is not None)
124
 
 
125
 
        create_time = self.stack.response_xml_item(response, prefix,
126
 
                                                   "CreationTime")
127
 
        self.assertTrue(self.time_re.match(create_time) is not None)
128
 
 
129
 
        description = self.stack.response_xml_item(response, prefix,
130
 
                                                   "TemplateDescription")
131
 
        self.assertTrue(self.description_re.match(description) is not None)
132
 
 
133
 
        status_reason = self.stack.response_xml_item(response, prefix,
134
 
                                                     "StackStatusReason")
135
 
        self.assertEqual(status_reason, self.stack_status_reason)
136
 
 
137
 
        stack_name = self.stack.response_xml_item(response, prefix,
138
 
                                                  "StackName")
139
 
        self.assertEqual(stack_name, self.stack.stackname)
140
 
 
141
 
        stack_status = self.stack.response_xml_item(response, prefix,
142
 
                                                    "StackStatus")
143
 
        self.assertEqual(stack_status, self.stack_status)
144
 
 
145
 
        print "ListStacks : OK"
146
 
 
147
 
    def testDescribeStacks(self):
148
 
        parameters = {}
149
 
        parameters['StackName'] = self.stack.stackname
150
 
        response = self.stack.heatclient.describe_stacks(**parameters)
151
 
        prefix = '/DescribeStacksResponse/DescribeStacksResult/Stacks/member'
152
 
 
153
 
        stack_id = self.stack.response_xml_item(response, prefix, "StackId")
154
 
        self.stack.check_stackid(stack_id)
155
 
 
156
 
        update_time = self.stack.response_xml_item(response, prefix,
157
 
                                                   "LastUpdatedTime")
158
 
        self.assertTrue(self.time_re.match(update_time) is not None)
159
 
 
160
 
        create_time = self.stack.response_xml_item(response, prefix,
161
 
                                                   "CreationTime")
162
 
        self.assertTrue(self.time_re.match(create_time) is not None)
163
 
 
164
 
        description = self.stack.response_xml_item(response, prefix,
165
 
                                                   "Description")
166
 
        self.assertTrue(self.description_re.match(description) is not None)
167
 
 
168
 
        status_reason = self.stack.response_xml_item(response, prefix,
169
 
                                                     "StackStatusReason")
170
 
        self.assertEqual(status_reason, self.stack_status_reason)
171
 
 
172
 
        stack_name = self.stack.response_xml_item(response, prefix,
173
 
                                                  "StackName")
174
 
        self.assertEqual(stack_name, self.stack.stackname)
175
 
 
176
 
        stack_status = self.stack.response_xml_item(response, prefix,
177
 
                                                    "StackStatus")
178
 
        self.assertEqual(stack_status, self.stack_status)
179
 
 
180
 
        stack_timeout = self.stack.response_xml_item(response, prefix,
181
 
                                                     "TimeoutInMinutes")
182
 
        self.assertEqual(stack_timeout, self.stack_timeout)
183
 
 
184
 
        disable_rollback = self.stack.response_xml_item(response, prefix,
185
 
                                                        "DisableRollback")
186
 
        self.assertEqual(disable_rollback, self.stack_disable_rollback)
187
 
 
188
 
        # Create a dict to lookup the expected template parameters
189
 
        # NoEcho parameters are masked with 6 asterisks
190
 
        template_parameters = {'DBUsername': '******',
191
 
                               'LinuxDistribution': 'F17',
192
 
                               'InstanceType': 'm1.xlarge',
193
 
                               'DBRootPassword': '******',
194
 
                               'KeyName': self.stack.keyname,
195
 
                               'DBPassword': '******',
196
 
                               'DBName': 'wordpress'}
197
 
 
198
 
        # We do a fully qualified xpath lookup to extract the paramter
199
 
        # value for each key, then check the extracted value
200
 
        param_prefix = prefix + "/Parameters/member"
201
 
        for key, value in template_parameters.iteritems():
202
 
            lookup = '[ParameterKey="' + key + '" and ParameterValue="' +\
203
 
                     value + '"]'
204
 
            lookup_value = self.stack.response_xml_item(response,
205
 
                                                        param_prefix + lookup,
206
 
                                                        "ParameterValue")
207
 
            self.assertEqual(lookup_value, value)
208
 
 
209
 
        # Then to a similar lookup to verify the Outputs section
210
 
        expected_url = "http://" + self.WikiDatabase.ip + "/wordpress"
211
 
 
212
 
        outputs_prefix = prefix + "/Outputs/member"
213
 
        lookup = '[OutputKey="WebsiteURL" and OutputValue="' + expected_url +\
214
 
                 '" and Description="URL for Wordpress wiki"]'
215
 
        lookup_value = self.stack.response_xml_item(response,
216
 
                                                    outputs_prefix + lookup,
217
 
                                                    "OutputValue")
218
 
        self.assertEqual(lookup_value, expected_url)
219
 
 
220
 
        print "DescribeStacks : OK"
221
 
 
222
 
    def testDescribeStackEvents(self):
223
 
        parameters = {}
224
 
        parameters['StackName'] = self.stack.stackname
225
 
        response = self.stack.heatclient.list_stack_events(**parameters)
226
 
        prefix = '/DescribeStackEventsResponse/DescribeStackEventsResult/' +\
227
 
                 'StackEvents/member[LogicalResourceId="' +\
228
 
                 self.logical_resource_name + '" and ResourceStatus="' +\
229
 
                 self.logical_resource_status + '"]'
230
 
 
231
 
        stack_id = self.stack.response_xml_item(response, prefix, "StackId")
232
 
        self.stack.check_stackid(stack_id)
233
 
 
234
 
        event_id = self.stack.response_xml_item(response, prefix, "EventId")
235
 
        self.assertTrue(re.match("[0-9]*$", event_id) is not None)
236
 
 
237
 
        resource_status = self.stack.response_xml_item(response, prefix,
238
 
                                                       "ResourceStatus")
239
 
        self.assertEqual(resource_status, self.logical_resource_status)
240
 
 
241
 
        resource_type = self.stack.response_xml_item(response, prefix,
242
 
                                                     "ResourceType")
243
 
        self.assertEqual(resource_type, self.logical_resource_type)
244
 
 
245
 
        update_time = self.stack.response_xml_item(response, prefix,
246
 
                                                   "Timestamp")
247
 
        self.assertTrue(self.time_re.match(update_time) is not None)
248
 
 
249
 
        status_data = self.stack.response_xml_item(response, prefix,
250
 
                                                   "ResourceStatusReason")
251
 
        self.assertEqual(status_data, "state changed")
252
 
 
253
 
        stack_name = self.stack.response_xml_item(response, prefix,
254
 
                                                  "StackName")
255
 
        self.assertEqual(stack_name, self.stack.stackname)
256
 
 
257
 
        log_res_id = self.stack.response_xml_item(response, prefix,
258
 
                                                  "LogicalResourceId")
259
 
        self.assertEqual(log_res_id, self.logical_resource_name)
260
 
 
261
 
        phys_res_id = self.stack.response_xml_item(response, prefix,
262
 
                                                   "PhysicalResourceId")
263
 
        self.assertTrue(self.phys_res_id_re.match(phys_res_id) is not None)
264
 
 
265
 
        # ResourceProperties format is defined as a string "blob" by AWS
266
 
        # we return JSON encoded properies, so decode and check one key
267
 
        prop_json = self.stack.response_xml_item(response, prefix,
268
 
                                                 "ResourceProperties")
269
 
        self.assertTrue(prop_json is not None)
270
 
 
271
 
        prop = json.loads(prop_json)
272
 
        self.assertEqual(prop["InstanceType"], "m1.xlarge")
273
 
 
274
 
        print "DescribeStackEvents : OK"
275
 
 
276
 
    def testGetTemplate(self):
277
 
        parameters = {}
278
 
        parameters['StackName'] = self.stack.stackname
279
 
        response = self.stack.heatclient.get_template(**parameters)
280
 
        prefix = '/GetTemplateResponse/GetTemplateResult'
281
 
 
282
 
        # Extract the JSON TemplateBody and prove it parses
283
 
        template = self.stack.response_xml_item(response, prefix,
284
 
                                                "TemplateBody")
285
 
        json_load = template_format.parse(template)
286
 
        self.assertTrue(json_load is not None)
287
 
 
288
 
        # Then sanity check content - I guess we could diff
289
 
        # with the template file but for now just check the
290
 
        # description looks sane..
291
 
        description = json_load['Description']
292
 
        self.assertTrue(self.description_re.match(description) is not None)
293
 
 
294
 
        print "GetTemplate : OK"
295
 
 
296
 
    def testDescribeStackResource(self):
297
 
        parameters = {'StackName': self.stack.stackname,
298
 
                      'LogicalResourceId': self.logical_resource_name}
299
 
        response = self.stack.heatclient.describe_stack_resource(**parameters)
300
 
        prefix = '/DescribeStackResourceResponse/DescribeStackResourceResult'\
301
 
                 + '/StackResourceDetail'
302
 
 
303
 
        stack_id = self.stack.response_xml_item(response, prefix, "StackId")
304
 
        self.stack.check_stackid(stack_id)
305
 
 
306
 
        resource_status = self.stack.response_xml_item(response, prefix,
307
 
                                                       "ResourceStatus")
308
 
        self.assertEqual(resource_status, self.logical_resource_status)
309
 
 
310
 
        resource_type = self.stack.response_xml_item(response, prefix,
311
 
                                                     "ResourceType")
312
 
        self.assertEqual(resource_type, self.logical_resource_type)
313
 
 
314
 
        update_time = self.stack.response_xml_item(response, prefix,
315
 
                                                   "LastUpdatedTimestamp")
316
 
        self.assertTrue(self.time_re.match(update_time) is not None)
317
 
 
318
 
        status_reason = self.stack.response_xml_item(response, prefix,
319
 
                                                     "ResourceStatusReason")
320
 
        self.assertEqual(status_reason, "state changed")
321
 
 
322
 
        stack_name = self.stack.response_xml_item(response, prefix,
323
 
                                                  "StackName")
324
 
        self.assertEqual(stack_name, self.stack.stackname)
325
 
 
326
 
        log_res_id = self.stack.response_xml_item(response, prefix,
327
 
                                                  "LogicalResourceId")
328
 
        self.assertEqual(log_res_id, self.logical_resource_name)
329
 
 
330
 
        phys_res_id = self.stack.response_xml_item(response, prefix,
331
 
                                                   "PhysicalResourceId")
332
 
        self.assertTrue(self.phys_res_id_re.match(phys_res_id) is not None)
333
 
 
334
 
        metadata = self.stack.response_xml_item(response, prefix, "Metadata")
335
 
        json_load = json.loads(metadata)
336
 
        self.assertTrue(json_load is not None)
337
 
        self.assertTrue("AWS::CloudFormation::Init" in json_load)
338
 
 
339
 
        print "DescribeStackResource : OK"
340
 
 
341
 
    def testDescribeStackResources(self):
342
 
        parameters = {'NameOrPid': self.stack.stackname,
343
 
                      'LogicalResourceId': self.logical_resource_name}
344
 
        response = self.stack.heatclient.describe_stack_resources(**parameters)
345
 
        prefix = '/DescribeStackResourcesResponse/' +\
346
 
                 'DescribeStackResourcesResult/StackResources/member'
347
 
 
348
 
        stack_id = self.stack.response_xml_item(response, prefix, "StackId")
349
 
        self.stack.check_stackid(stack_id)
350
 
 
351
 
        resource_status = self.stack.response_xml_item(response, prefix,
352
 
                                                       "ResourceStatus")
353
 
        self.assertEqual(resource_status, self.logical_resource_status)
354
 
 
355
 
        resource_type = self.stack.response_xml_item(response, prefix,
356
 
                                                     "ResourceType")
357
 
        self.assertEqual(resource_type, self.logical_resource_type)
358
 
 
359
 
        update_time = self.stack.response_xml_item(response, prefix,
360
 
                                                   "Timestamp")
361
 
        self.assertTrue(self.time_re.match(update_time) is not None)
362
 
 
363
 
        status_reason = self.stack.response_xml_item(response, prefix,
364
 
                                                     "ResourceStatusReason")
365
 
        self.assertEqual(status_reason, "state changed")
366
 
 
367
 
        stack_name = self.stack.response_xml_item(response, prefix,
368
 
                                                  "StackName")
369
 
        self.assertEqual(stack_name, self.stack.stackname)
370
 
 
371
 
        log_res_id = self.stack.response_xml_item(response, prefix,
372
 
                                                  "LogicalResourceId")
373
 
        self.assertEqual(log_res_id, self.logical_resource_name)
374
 
 
375
 
        phys_res_id = self.stack.response_xml_item(response, prefix,
376
 
                                                   "PhysicalResourceId")
377
 
        self.assertTrue(self.phys_res_id_re.match(phys_res_id) is not None)
378
 
 
379
 
        print "DescribeStackResources : OK"
380
 
 
381
 
    def testListStackResources(self):
382
 
        parameters = {}
383
 
        parameters['StackName'] = self.stack.stackname
384
 
        response = self.stack.heatclient.list_stack_resources(**parameters)
385
 
        prefix = '/ListStackResourcesResponse/ListStackResourcesResult' +\
386
 
                 '/StackResourceSummaries/member'
387
 
 
388
 
        resource_status = self.stack.response_xml_item(response, prefix,
389
 
                                                       "ResourceStatus")
390
 
        self.assertEqual(resource_status, self.logical_resource_status)
391
 
 
392
 
        status_reason = self.stack.response_xml_item(response, prefix,
393
 
                                                     "ResourceStatusReason")
394
 
        self.assertEqual(status_reason, "state changed")
395
 
 
396
 
        update_time = self.stack.response_xml_item(response, prefix,
397
 
                                                   "LastUpdatedTimestamp")
398
 
        self.assertTrue(self.time_re.match(update_time) is not None)
399
 
 
400
 
        resource_type = self.stack.response_xml_item(response, prefix,
401
 
                                                     "ResourceType")
402
 
        self.assertEqual(resource_type, self.logical_resource_type)
403
 
 
404
 
        log_res_id = self.stack.response_xml_item(response, prefix,
405
 
                                                  "LogicalResourceId")
406
 
        self.assertEqual(log_res_id, self.logical_resource_name)
407
 
 
408
 
        phys_res_id = self.stack.response_xml_item(response, prefix,
409
 
                                                   "PhysicalResourceId")
410
 
        self.assertTrue(self.phys_res_id_re.match(phys_res_id) is not None)
411
 
 
412
 
        print "ListStackResources : OK"
413
 
 
414
 
    def testValidateTemplate(self):
415
 
        # Use stack.format_parameters to get the TemplateBody
416
 
        params = self.stack.format_parameters()
417
 
        val_params = {'TemplateBody': params['TemplateBody']}
418
 
        response = self.stack.heatclient.validate_template(**val_params)
419
 
        prefix = '/ValidateTemplateResponse/ValidateTemplateResult' +\
420
 
                 '/Parameters/member'
421
 
        # Check the response contains all the expected paramter keys
422
 
        templ_params = ['DBUsername', 'LinuxDistribution', 'InstanceType',
423
 
                        'DBRootPassword', 'KeyName', 'DBPassword', 'DBName']
424
 
 
425
 
        for param in templ_params:
426
 
            lookup = '[ParameterKey="' + param + '"]'
427
 
            val = self.stack.response_xml_item(response, prefix + lookup,
428
 
                                               'ParameterKey')
429
 
            self.assertEqual(param, val)
430
 
        print "ValidateTemplate : OK"