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
20 from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
21 from snaps.openstack.create_image import OpenStackImage
24 from urllib.request import URLError
26 from urllib2 import URLError
32 from snaps.openstack import create_stack
33 from snaps.openstack.create_stack import StackSettings, StackSettingsError
34 from snaps.openstack.tests import openstack_tests, create_instance_tests
35 from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
36 from snaps.openstack.utils import heat_utils, neutron_utils, nova_utils
38 __author__ = 'spisarski'
40 logger = logging.getLogger('create_stack_tests')
43 class StackSettingsUnitTests(unittest.TestCase):
45 Tests the construction of the StackSettings class
48 def test_no_params(self):
49 with self.assertRaises(StackSettingsError):
52 def test_empty_config(self):
53 with self.assertRaises(StackSettingsError):
54 StackSettings(**dict())
56 def test_name_only(self):
57 with self.assertRaises(StackSettingsError):
58 StackSettings(name='foo')
60 def test_config_with_name_only(self):
61 with self.assertRaises(StackSettingsError):
62 StackSettings(**{'name': 'foo'})
64 def test_config_minimum_template(self):
65 settings = StackSettings(**{'name': 'stack', 'template': 'foo'})
66 self.assertEqual('stack', settings.name)
67 self.assertEqual('foo', settings.template)
68 self.assertIsNone(settings.template_path)
69 self.assertIsNone(settings.env_values)
70 self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
71 settings.stack_create_timeout)
73 def test_config_minimum_template_path(self):
74 settings = StackSettings(**{'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,
80 settings.stack_create_timeout)
82 def test_minimum_template(self):
83 settings = StackSettings(name='stack', template='foo')
84 self.assertEqual('stack', settings.name)
85 self.assertEqual('foo', settings.template)
86 self.assertIsNone(settings.template_path)
87 self.assertIsNone(settings.env_values)
88 self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
89 settings.stack_create_timeout)
91 def test_minimum_template_path(self):
92 settings = StackSettings(name='stack', template_path='foo')
93 self.assertEqual('stack', settings.name)
94 self.assertEqual('foo', settings.template_path)
95 self.assertIsNone(settings.template)
96 self.assertIsNone(settings.env_values)
97 self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
98 settings.stack_create_timeout)
101 env_values = {'foo': 'bar'}
102 settings = StackSettings(name='stack', template='bar',
103 template_path='foo', env_values=env_values,
104 stack_create_timeout=999)
105 self.assertEqual('stack', settings.name)
106 self.assertEqual('bar', settings.template)
107 self.assertEqual('foo', settings.template_path)
108 self.assertEqual(env_values, settings.env_values)
109 self.assertEqual(999, settings.stack_create_timeout)
111 def test_config_all(self):
112 env_values = {'foo': 'bar'}
113 settings = StackSettings(
114 **{'name': 'stack', 'template': 'bar', 'template_path': 'foo',
115 'env_values': env_values, 'stack_create_timeout': 999})
116 self.assertEqual('stack', settings.name)
117 self.assertEqual('bar', settings.template)
118 self.assertEqual('foo', settings.template_path)
119 self.assertEqual(env_values, settings.env_values)
120 self.assertEqual(999, settings.stack_create_timeout)
123 class CreateStackSuccessTests(OSIntegrationTestCase):
125 Tests for the CreateStack class defined in create_stack.py
130 Instantiates the CreateStack object that is responsible for downloading
131 and creating an OS stack file within OpenStack
133 super(self.__class__, self).__start__()
135 self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
137 self.heat_creds = self.admin_os_creds
138 self.heat_creds.project_name = self.admin_os_creds.project_name
140 self.heat_cli = heat_utils.heat_client(self.heat_creds)
141 self.stack_creator = None
143 self.image_creator = OpenStackImage(
144 self.heat_creds, openstack_tests.cirros_image_settings(
145 name=self.guid + '-image',
146 image_metadata=self.image_metadata))
147 self.image_creator.create()
150 self.flavor_creator = OpenStackFlavor(
152 FlavorSettings(name=self.guid + '-flavor-name', ram=256, disk=10,
154 self.flavor_creator.create()
156 self.network_name = self.guid + '-net'
157 self.subnet_name = self.guid + '-subnet'
158 self.vm_inst_name = self.guid + '-inst'
161 'image_name': self.image_creator.image_settings.name,
162 'flavor_name': self.flavor_creator.flavor_settings.name,
163 'net_name': self.network_name,
164 'subnet_name': self.subnet_name,
165 'inst_name': self.vm_inst_name}
167 self.heat_tmplt_path = pkg_resources.resource_filename(
168 'snaps.openstack.tests.heat', 'test_heat_template.yaml')
172 Cleans the stack and downloaded stack file
174 if self.stack_creator:
176 self.stack_creator.clean()
180 if self.image_creator:
182 self.image_creator.clean()
186 if self.flavor_creator:
188 self.flavor_creator.clean()
192 super(self.__class__, self).__clean__()
194 def test_create_stack_template_file(self):
196 Tests the creation of an OpenStack stack from Heat template file.
199 # Set the default stack settings, then set any custom parameters sent
201 stack_settings = StackSettings(
202 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
203 template_path=self.heat_tmplt_path,
204 env_values=self.env_values)
205 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
207 created_stack = self.stack_creator.create()
208 self.assertIsNotNone(created_stack)
210 retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
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.assertEqual(0, len(self.stack_creator.get_outputs()))
217 def test_create_stack_template_dict(self):
219 Tests the creation of an OpenStack stack from a heat dict() object.
222 # Set the default stack settings, then set any custom parameters sent
224 template_dict = heat_utils.parse_heat_template_str(
225 file_utils.read_file(self.heat_tmplt_path))
226 stack_settings = StackSettings(
227 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
228 template=template_dict,
229 env_values=self.env_values)
230 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
232 created_stack = self.stack_creator.create()
233 self.assertIsNotNone(created_stack)
235 retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
237 self.assertIsNotNone(retrieved_stack)
238 self.assertEqual(created_stack.name, retrieved_stack.name)
239 self.assertEqual(created_stack.id, retrieved_stack.id)
240 self.assertEqual(0, len(self.stack_creator.get_outputs()))
242 def test_create_delete_stack(self):
244 Tests the creation then deletion of an OpenStack stack to ensure
245 clean() does not raise an Exception.
248 template_dict = heat_utils.parse_heat_template_str(
249 file_utils.read_file(self.heat_tmplt_path))
250 stack_settings = StackSettings(
251 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
252 template=template_dict,
253 env_values=self.env_values)
254 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
256 created_stack = self.stack_creator.create()
257 self.assertIsNotNone(created_stack)
259 retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
261 self.assertIsNotNone(retrieved_stack)
262 self.assertEqual(created_stack.name, retrieved_stack.name)
263 self.assertEqual(created_stack.id, retrieved_stack.id)
264 self.assertEqual(0, len(self.stack_creator.get_outputs()))
265 self.assertEqual(create_stack.STATUS_CREATE_COMPLETE,
266 self.stack_creator.get_status())
268 # Delete Stack manually
269 heat_utils.delete_stack(self.heat_cli, created_stack)
271 end_time = time.time() + 90
273 while time.time() < end_time:
274 status = heat_utils.get_stack_status(self.heat_cli,
276 if status == create_stack.STATUS_DELETE_COMPLETE:
280 self.assertTrue(deleted)
282 # Must not throw an exception when attempting to cleanup non-existent
284 self.stack_creator.clean()
285 self.assertIsNone(self.stack_creator.get_stack())
287 def test_create_same_stack(self):
289 Tests the creation of an OpenStack stack when the stack already exists.
292 template_dict = heat_utils.parse_heat_template_str(
293 file_utils.read_file(self.heat_tmplt_path))
294 stack_settings = StackSettings(
295 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
296 template=template_dict,
297 env_values=self.env_values)
298 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
300 created_stack1 = self.stack_creator.create()
302 retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
304 self.assertIsNotNone(retrieved_stack)
305 self.assertEqual(created_stack1.name, retrieved_stack.name)
306 self.assertEqual(created_stack1.id, retrieved_stack.id)
307 self.assertEqual(0, len(self.stack_creator.get_outputs()))
309 # Should be retrieving the instance data
310 stack_creator2 = create_stack.OpenStackHeatStack(self.heat_creds,
312 stack2 = stack_creator2.create()
313 self.assertEqual(created_stack1.id, stack2.id)
315 def test_retrieve_network_creators(self):
317 Tests the creation of an OpenStack stack from Heat template file and
318 the retrieval of the network creator.
320 stack_settings = StackSettings(
321 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
322 template_path=self.heat_tmplt_path,
323 env_values=self.env_values)
324 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
326 created_stack = self.stack_creator.create()
327 self.assertIsNotNone(created_stack)
329 net_creators = self.stack_creator.get_network_creators()
330 self.assertIsNotNone(net_creators)
331 self.assertEqual(1, len(net_creators))
332 self.assertEqual(self.network_name, net_creators[0].get_network().name)
334 neutron = neutron_utils.neutron_client(self.os_creds)
335 net_by_name = neutron_utils.get_network(
336 neutron, network_name=net_creators[0].get_network().name)
337 self.assertEqual(net_creators[0].get_network(), net_by_name)
338 self.assertIsNotNone(neutron_utils.get_network_by_id(
339 neutron, net_creators[0].get_network().id))
341 self.assertEqual(1, len(net_creators[0].get_subnets()))
342 subnet = net_creators[0].get_subnets()[0]
343 subnet_by_name = neutron_utils.get_subnet(
344 neutron, subnet_name=subnet.name)
345 self.assertEqual(subnet, subnet_by_name)
347 subnet_by_id = neutron_utils.get_subnet_by_id(neutron, subnet.id)
348 self.assertIsNotNone(subnet_by_id)
349 self.assertEqual(subnet_by_name, subnet_by_id)
351 def test_retrieve_vm_inst_creators(self):
353 Tests the creation of an OpenStack stack from Heat template file and
354 the retrieval of the network creator.
356 stack_settings = StackSettings(
357 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
358 template_path=self.heat_tmplt_path,
359 env_values=self.env_values)
360 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
362 created_stack = self.stack_creator.create()
363 self.assertIsNotNone(created_stack)
365 vm_inst_creators = self.stack_creator.get_vm_inst_creators()
366 self.assertIsNotNone(vm_inst_creators)
367 self.assertEqual(1, len(vm_inst_creators))
368 self.assertEqual(self.vm_inst_name,
369 vm_inst_creators[0].get_vm_inst().name)
371 nova = nova_utils.nova_client(self.admin_os_creds)
372 vm_inst_by_name = nova_utils.get_server(
373 nova, server_name=vm_inst_creators[0].get_vm_inst().name)
374 self.assertEqual(vm_inst_creators[0].get_vm_inst(), vm_inst_by_name)
375 self.assertIsNotNone(nova_utils.get_server_object_by_id(
376 nova, vm_inst_creators[0].get_vm_inst().id))
379 class CreateComplexStackTests(OSIntegrationTestCase):
381 Tests for the CreateStack class defined in create_stack.py
386 Instantiates the CreateStack object that is responsible for downloading
387 and creating an OS stack file within OpenStack
389 super(self.__class__, self).__start__()
391 self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
393 self.heat_creds = self.admin_os_creds
394 self.heat_creds.project_name = self.admin_os_creds.project_name
396 self.heat_cli = heat_utils.heat_client(self.heat_creds)
397 self.stack_creator = None
399 self.image_creator = OpenStackImage(
400 self.heat_creds, openstack_tests.cirros_image_settings(
401 name=self.guid + '-image',
402 image_metadata=self.image_metadata))
403 self.image_creator.create()
405 self.network_name = self.guid + '-net'
406 self.subnet_name = self.guid + '-subnet'
407 self.flavor1_name = self.guid + '-flavor1'
408 self.flavor2_name = self.guid + '-flavor2'
409 self.vm_inst1_name = self.guid + '-inst1'
410 self.vm_inst2_name = self.guid + '-inst2'
411 self.keypair_name = self.guid + '-kp'
414 'image1_name': self.image_creator.image_settings.name,
415 'image2_name': self.image_creator.image_settings.name,
416 'flavor1_name': self.flavor1_name,
417 'flavor2_name': self.flavor2_name,
418 'net_name': self.network_name,
419 'subnet_name': self.subnet_name,
420 'inst1_name': self.vm_inst1_name,
421 'inst2_name': self.vm_inst2_name,
422 'keypair_name': self.keypair_name}
424 self.heat_tmplt_path = pkg_resources.resource_filename(
425 'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
429 Cleans the stack and downloaded stack file
431 if self.stack_creator:
433 self.stack_creator.clean()
437 if self.image_creator:
439 self.image_creator.clean()
443 super(self.__class__, self).__clean__()
445 def test_connect_via_ssh_heat_vm(self):
447 Tests the creation of an OpenStack stack from Heat template file and
448 the retrieval of two VM instance creators and attempt to connect via
449 SSH to the first one with a floating IP.
451 stack_settings = StackSettings(
452 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
453 template_path=self.heat_tmplt_path,
454 env_values=self.env_values)
455 self.stack_creator = create_stack.OpenStackHeatStack(
456 self.heat_creds, stack_settings,
457 [self.image_creator.image_settings])
458 created_stack = self.stack_creator.create()
459 self.assertIsNotNone(created_stack)
461 vm_inst_creators = self.stack_creator.get_vm_inst_creators(
462 heat_keypair_option='private_key')
463 self.assertIsNotNone(vm_inst_creators)
464 self.assertEqual(2, len(vm_inst_creators))
466 for vm_inst_creator in vm_inst_creators:
467 if vm_inst_creator.get_vm_inst().name == self.vm_inst1_name:
469 create_instance_tests.validate_ssh_client(vm_inst_creator))
471 vm_settings = vm_inst_creator.instance_settings
472 self.assertEqual(0, len(vm_settings.floating_ip_settings))
475 class CreateStackNegativeTests(OSIntegrationTestCase):
477 Negative test cases for the CreateStack class
481 super(self.__class__, self).__start__()
483 self.heat_creds = self.admin_os_creds
484 self.heat_creds.project_name = self.admin_os_creds.project_name
486 self.stack_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
487 self.stack_creator = None
488 self.heat_tmplt_path = pkg_resources.resource_filename(
489 'snaps.openstack.tests.heat', 'test_heat_template.yaml')
492 if self.stack_creator:
493 self.stack_creator.clean()
494 super(self.__class__, self).__clean__()
496 def test_missing_dependencies(self):
498 Expect an StackCreationError when the stack file does not exist
500 stack_settings = StackSettings(name=self.stack_name,
501 template_path=self.heat_tmplt_path)
502 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
504 with self.assertRaises(HTTPBadRequest):
505 self.stack_creator.create()
507 def test_bad_stack_file(self):
509 Expect an StackCreationError when the stack file does not exist
511 stack_settings = StackSettings(name=self.stack_name,
513 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
515 with self.assertRaises(IOError):
516 self.stack_creator.create()