1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 # and others. All rights reserved.
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:
8 # http://www.apache.org/licenses/LICENSE-2.0
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.
18 from heatclient.exc import HTTPBadRequest
19 from snaps import file_utils
21 from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
23 from snaps.openstack.create_image import OpenStackImage
26 from urllib.request import URLError
28 from urllib2 import URLError
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
40 __author__ = 'spisarski'
42 logger = logging.getLogger('create_stack_tests')
45 class StackSettingsUnitTests(unittest.TestCase):
47 Tests the construction of the StackSettings class
49 def test_no_params(self):
50 with self.assertRaises(StackSettingsError):
53 def test_empty_config(self):
54 with self.assertRaises(StackSettingsError):
55 StackSettings(config=dict())
57 def test_name_only(self):
58 with self.assertRaises(StackSettingsError):
59 StackSettings(name='foo')
61 def test_config_with_name_only(self):
62 with self.assertRaises(StackSettingsError):
63 StackSettings(config={'name': 'foo'})
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)
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)
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)
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)
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)
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)
119 class CreateStackSuccessTests(OSIntegrationTestCase):
121 Test for the CreateStack class defined in create_stack.py
126 Instantiates the CreateStack object that is responsible for downloading and creating an OS stack file
129 super(self.__class__, self).__start__()
131 self.guid = str(uuid.uuid4())
132 self.heat_cli = heat_utils.heat_client(self.os_creds)
133 self.stack_creator = None
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()
141 self.flavor_creator = OpenStackFlavor(
143 FlavorSettings(name=self.guid + '-flavor-name', ram=128, disk=10, vcpus=1))
144 self.flavor_creator.create()
146 self.env_values = {'image_name': self.image_creator.image_settings.name,
147 'flavor_name': self.flavor_creator.flavor_settings.name}
149 self.heat_tmplt_path = pkg_resources.resource_filename(
150 'snaps.openstack.tests.heat', 'test_heat_template.yaml')
154 Cleans the stack and downloaded stack file
156 if self.stack_creator:
158 self.stack_creator.clean()
162 if self.image_creator:
164 self.image_creator.clean()
168 if self.flavor_creator:
170 self.flavor_creator.clean()
174 super(self.__class__, self).__clean__()
176 def test_create_stack_template_file(self):
178 Tests the creation of an OpenStack stack from Heat template file.
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)
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()))
196 def test_create_stack_template_dict(self):
198 Tests the creation of an OpenStack stack from a heat dict() object.
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)
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()))
218 def test_create_delete_stack(self):
220 Tests the creation then deletion of an OpenStack stack to ensure clean() does not raise an Exception.
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)
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())
240 # Delete Stack manually
241 heat_utils.delete_stack(self.heat_cli, created_stack)
243 end_time = time.time() + 90
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:
251 self.assertTrue(deleted)
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())
257 def test_create_same_stack(self):
259 Tests the creation of an OpenStack stack when the stack already exists.
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()
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()))
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)
283 class CreateStackNegativeTests(OSIntegrationTestCase):
285 Negative test cases for the CreateStack class
289 super(self.__class__, self).__start__()
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')
297 if self.stack_creator:
298 self.stack_creator.clean()
299 super(self.__class__, self).__clean__()
301 def test_missing_dependencies(self):
303 Expect an StackCreationError when the stack file does not exist
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()
310 def test_bad_stack_file(self):
312 Expect an StackCreationError when the stack file does not exist
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()