Merge "Ignore several files"
[snaps.git] / snaps / openstack / tests / create_stack_tests.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import pkg_resources
16 import time
17
18 from heatclient.exc import HTTPBadRequest
19 from snaps import file_utils
20
21 from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
22
23 from snaps.openstack.create_image import OpenStackImage
24
25 try:
26     from urllib.request import URLError
27 except ImportError:
28     from urllib2 import URLError
29
30 import logging
31 import unittest
32 import uuid
33
34 from snaps.openstack import create_stack
35 from snaps.openstack.create_stack import StackSettings, StackSettingsError
36 from snaps.openstack.tests import openstack_tests
37 from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
38 from snaps.openstack.utils import heat_utils
39
40 __author__ = 'spisarski'
41
42 logger = logging.getLogger('create_stack_tests')
43
44
45 class StackSettingsUnitTests(unittest.TestCase):
46     """
47     Tests the construction of the StackSettings class
48     """
49     def test_no_params(self):
50         with self.assertRaises(StackSettingsError):
51             StackSettings()
52
53     def test_empty_config(self):
54         with self.assertRaises(StackSettingsError):
55             StackSettings(config=dict())
56
57     def test_name_only(self):
58         with self.assertRaises(StackSettingsError):
59             StackSettings(name='foo')
60
61     def test_config_with_name_only(self):
62         with self.assertRaises(StackSettingsError):
63             StackSettings(config={'name': 'foo'})
64
65     def test_config_minimum_template(self):
66         settings = StackSettings(config={'name': 'stack', 'template': 'foo'})
67         self.assertEqual('stack', settings.name)
68         self.assertEqual('foo', settings.template)
69         self.assertIsNone(settings.template_path)
70         self.assertIsNone(settings.env_values)
71         self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT, settings.stack_create_timeout)
72
73     def test_config_minimum_template_path(self):
74         settings = StackSettings(config={'name': 'stack', 'template_path': 'foo'})
75         self.assertEqual('stack', settings.name)
76         self.assertIsNone(settings.template)
77         self.assertEqual('foo', settings.template_path)
78         self.assertIsNone(settings.env_values)
79         self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT, settings.stack_create_timeout)
80
81     def test_minimum_template(self):
82         settings = StackSettings(name='stack', template='foo')
83         self.assertEqual('stack', settings.name)
84         self.assertEqual('foo', settings.template)
85         self.assertIsNone(settings.template_path)
86         self.assertIsNone(settings.env_values)
87         self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT, settings.stack_create_timeout)
88
89     def test_minimum_template_path(self):
90         settings = StackSettings(name='stack', template_path='foo')
91         self.assertEqual('stack', settings.name)
92         self.assertEqual('foo', settings.template_path)
93         self.assertIsNone(settings.template)
94         self.assertIsNone(settings.env_values)
95         self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT, settings.stack_create_timeout)
96
97     def test_all(self):
98         env_values = {'foo': 'bar'}
99         settings = StackSettings(name='stack', template='bar', template_path='foo', env_values=env_values,
100                                  stack_create_timeout=999)
101         self.assertEqual('stack', settings.name)
102         self.assertEqual('bar', settings.template)
103         self.assertEqual('foo', settings.template_path)
104         self.assertEqual(env_values, settings.env_values)
105         self.assertEqual(999, settings.stack_create_timeout)
106
107     def test_config_all(self):
108         env_values = {'foo': 'bar'}
109         settings = StackSettings(
110             config={'name': 'stack', 'template': 'bar', 'template_path': 'foo',
111                     'env_values': env_values, 'stack_create_timeout': 999})
112         self.assertEqual('stack', settings.name)
113         self.assertEqual('bar', settings.template)
114         self.assertEqual('foo', settings.template_path)
115         self.assertEqual(env_values, settings.env_values)
116         self.assertEqual(999, settings.stack_create_timeout)
117
118
119 class CreateStackSuccessTests(OSIntegrationTestCase):
120     """
121     Test for the CreateStack class defined in create_stack.py
122     """
123
124     def setUp(self):
125         """
126         Instantiates the CreateStack object that is responsible for downloading and creating an OS stack file
127         within OpenStack
128         """
129         super(self.__class__, self).__start__()
130
131         self.guid = str(uuid.uuid4())
132         self.heat_cli = heat_utils.heat_client(self.os_creds)
133         self.stack_creator = None
134
135         self.image_creator = OpenStackImage(
136             self.os_creds, openstack_tests.cirros_image_settings(
137                 name=self.__class__.__name__ + '-' + str(self.guid) + '-image', image_metadata=self.image_metadata))
138         self.image_creator.create()
139
140         # Create Flavor
141         self.flavor_creator = OpenStackFlavor(
142             self.admin_os_creds,
143             FlavorSettings(name=self.guid + '-flavor-name', ram=128, disk=10, vcpus=1))
144         self.flavor_creator.create()
145
146         self.env_values = {'image_name': self.image_creator.image_settings.name,
147                            'flavor_name': self.flavor_creator.flavor_settings.name}
148
149         self.heat_tmplt_path = pkg_resources.resource_filename(
150             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
151
152     def tearDown(self):
153         """
154         Cleans the stack and downloaded stack file
155         """
156         if self.stack_creator:
157             try:
158                 self.stack_creator.clean()
159             except:
160                 pass
161
162         if self.image_creator:
163             try:
164                 self.image_creator.clean()
165             except:
166                 pass
167
168         if self.flavor_creator:
169             try:
170                 self.flavor_creator.clean()
171             except:
172                 pass
173
174         super(self.__class__, self).__clean__()
175
176     def test_create_stack_template_file(self):
177         """
178         Tests the creation of an OpenStack stack from Heat template file.
179         """
180         # Create Stack
181         # Set the default stack settings, then set any custom parameters sent from the app
182         stack_settings = StackSettings(name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
183                                        template_path=self.heat_tmplt_path,
184                                        env_values=self.env_values)
185         self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
186         created_stack = self.stack_creator.create()
187         self.assertIsNotNone(created_stack)
188
189         retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack.id)
190         self.assertIsNotNone(retrieved_stack)
191         self.assertEqual(created_stack.name, retrieved_stack.name)
192         self.assertEqual(created_stack.id, retrieved_stack.id)
193         self.assertIsNotNone(self.stack_creator.get_outputs())
194         self.assertEquals(0, len(self.stack_creator.get_outputs()))
195
196     def test_create_stack_template_dict(self):
197         """
198         Tests the creation of an OpenStack stack from a heat dict() object.
199         """
200         # Create Stack
201         # Set the default stack settings, then set any custom parameters sent from the app
202         template_dict = heat_utils.parse_heat_template_str(
203             file_utils.read_file(self.heat_tmplt_path))
204         stack_settings = StackSettings(name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
205                                        template=template_dict,
206                                        env_values=self.env_values)
207         self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
208         created_stack = self.stack_creator.create()
209         self.assertIsNotNone(created_stack)
210
211         retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack.id)
212         self.assertIsNotNone(retrieved_stack)
213         self.assertEqual(created_stack.name, retrieved_stack.name)
214         self.assertEqual(created_stack.id, retrieved_stack.id)
215         self.assertIsNotNone(self.stack_creator.get_outputs())
216         self.assertEquals(0, len(self.stack_creator.get_outputs()))
217
218     def test_create_delete_stack(self):
219         """
220         Tests the creation then deletion of an OpenStack stack to ensure clean() does not raise an Exception.
221         """
222         # Create Stack
223         template_dict = heat_utils.parse_heat_template_str(
224             file_utils.read_file(self.heat_tmplt_path))
225         stack_settings = StackSettings(name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
226                                        template=template_dict,
227                                        env_values=self.env_values)
228         self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
229         created_stack = self.stack_creator.create()
230         self.assertIsNotNone(created_stack)
231
232         retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack.id)
233         self.assertIsNotNone(retrieved_stack)
234         self.assertEqual(created_stack.name, retrieved_stack.name)
235         self.assertEqual(created_stack.id, retrieved_stack.id)
236         self.assertIsNotNone(self.stack_creator.get_outputs())
237         self.assertEquals(0, len(self.stack_creator.get_outputs()))
238         self.assertEqual(create_stack.STATUS_CREATE_COMPLETE, self.stack_creator.get_status())
239
240         # Delete Stack manually
241         heat_utils.delete_stack(self.heat_cli, created_stack)
242
243         end_time = time.time() + 90
244         deleted = False
245         while time.time() < end_time:
246             status = heat_utils.get_stack_status(self.heat_cli, retrieved_stack.id)
247             if status == create_stack.STATUS_DELETE_COMPLETE:
248                 deleted = True
249                 break
250
251         self.assertTrue(deleted)
252
253         # Must not throw an exception when attempting to cleanup non-existent stack
254         self.stack_creator.clean()
255         self.assertIsNone(self.stack_creator.get_stack())
256
257     def test_create_same_stack(self):
258         """
259         Tests the creation of an OpenStack stack when the stack already exists.
260         """
261         # Create Stack
262         template_dict = heat_utils.parse_heat_template_str(
263             file_utils.read_file(self.heat_tmplt_path))
264         stack_settings = StackSettings(name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
265                                        template=template_dict,
266                                        env_values=self.env_values)
267         self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
268         created_stack1 = self.stack_creator.create()
269
270         retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack1.id)
271         self.assertIsNotNone(retrieved_stack)
272         self.assertEqual(created_stack1.name, retrieved_stack.name)
273         self.assertEqual(created_stack1.id, retrieved_stack.id)
274         self.assertIsNotNone(self.stack_creator.get_outputs())
275         self.assertEqual(0, len(self.stack_creator.get_outputs()))
276
277         # Should be retrieving the instance data
278         stack_creator2 = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
279         stack2 = stack_creator2.create()
280         self.assertEqual(created_stack1.id, stack2.id)
281
282
283 class CreateStackNegativeTests(OSIntegrationTestCase):
284     """
285     Negative test cases for the CreateStack class
286     """
287
288     def setUp(self):
289         super(self.__class__, self).__start__()
290
291         self.stack_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
292         self.stack_creator = None
293         self.heat_tmplt_path = pkg_resources.resource_filename(
294             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
295
296     def tearDown(self):
297         if self.stack_creator:
298             self.stack_creator.clean()
299         super(self.__class__, self).__clean__()
300
301     def test_missing_dependencies(self):
302         """
303         Expect an StackCreationError when the stack file does not exist
304         """
305         stack_settings = StackSettings(name=self.stack_name, template_path=self.heat_tmplt_path)
306         self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
307         with self.assertRaises(HTTPBadRequest):
308             self.stack_creator.create()
309
310     def test_bad_stack_file(self):
311         """
312         Expect an StackCreationError when the stack file does not exist
313         """
314         stack_settings = StackSettings(name=self.stack_name, template_path='foo')
315         self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
316         with self.assertRaises(IOError):
317             self.stack_creator.create()