53ac3dd09871baf2716d015f1f8130511a39da1f
[snaps.git] / snaps / openstack / utils / tests / heat_utils_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 logging
16 import os
17
18 import pkg_resources
19 import uuid
20
21 import time
22
23 import snaps.config.stack as stack_config
24 from snaps.config.flavor import FlavorConfig
25 from snaps.openstack.create_flavor import OpenStackFlavor
26
27 from snaps.openstack.create_image import OpenStackImage
28 from snaps.openstack.create_instance import OpenStackVmInstance
29 from snaps.openstack.create_stack import StackConfig
30 from snaps.openstack.tests import openstack_tests
31 from snaps.openstack.tests.os_source_file_test import OSComponentTestCase
32 from snaps.openstack.utils import (
33     heat_utils, neutron_utils, nova_utils, settings_utils, glance_utils,
34     cinder_utils, keystone_utils)
35
36 __author__ = 'spisarski'
37
38 logger = logging.getLogger('heat_utils_tests')
39
40
41 class HeatSmokeTests(OSComponentTestCase):
42     """
43     Tests to ensure that the heat client can communicate with the cloud
44     """
45
46     def test_heat_connect_success(self):
47         """
48         Tests to ensure that the proper credentials can connect.
49         """
50         heat = heat_utils.heat_client(self.os_creds, self.os_session)
51
52         # This should not throw an exception
53         stacks = heat.stacks.list()
54         for stack in stacks:
55             logger.info('Stack - %s', stack)
56
57     def test_heat_connect_fail(self):
58         """
59         Tests to ensure that the improper credentials cannot connect.
60         """
61         from snaps.openstack.os_credentials import OSCreds
62
63         heat = heat_utils.heat_client(
64             OSCreds(username='user', password='pass',
65                     auth_url=self.os_creds.auth_url,
66                     project_name=self.os_creds.project_name,
67                     proxy_settings=self.os_creds.proxy_settings))
68         stacks = heat.stacks.list()
69
70         # This should throw an exception
71         with self.assertRaises(Exception):
72             for stack in stacks:
73                 logger.info('Stack - %s', stack)
74
75
76 class HeatUtilsCreateSimpleStackTests(OSComponentTestCase):
77     """
78     Test basic Heat functionality
79     """
80
81     def setUp(self):
82         """
83         Instantiates OpenStack instances that cannot be spawned by Heat
84         """
85         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
86         stack_name1 = guid + '-stack1'
87         stack_name2 = guid + '-stack2'
88         self.network_name = guid + '-net'
89         self.subnet_name = guid + '-subnet'
90         self.vm_inst_name = guid + '-inst'
91
92         self.image_creator = OpenStackImage(
93             self.os_creds, openstack_tests.cirros_image_settings(
94                 name=guid + '-image', image_metadata=self.image_metadata))
95         self.image_creator.create()
96
97         # Create Flavor
98         self.flavor_creator = OpenStackFlavor(
99             self.os_creds,
100             FlavorConfig(name=guid + '-flavor', ram=256, disk=10, vcpus=1))
101         self.flavor_creator.create()
102
103         env_values = {'image_name': self.image_creator.image_settings.name,
104                       'flavor_name': self.flavor_creator.flavor_settings.name,
105                       'net_name': self.network_name,
106                       'subnet_name': self.subnet_name,
107                       'inst_name': self.vm_inst_name}
108         heat_tmplt_path = pkg_resources.resource_filename(
109             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
110         self.stack_settings1 = StackConfig(
111             name=stack_name1, template_path=heat_tmplt_path,
112             env_values=env_values)
113         self.stack_settings2 = StackConfig(
114             name=stack_name2, template_path=heat_tmplt_path,
115             env_values=env_values)
116         self.stack1 = None
117         self.stack2 = None
118         self.heat_client = heat_utils.heat_client(
119             self.os_creds, self.os_session)
120
121     def tearDown(self):
122         """
123         Cleans the stack and image
124         """
125         if self.stack1:
126             try:
127                 heat_utils.delete_stack(self.heat_client, self.stack1)
128             except:
129                 pass
130
131         if self.stack2:
132             try:
133                 heat_utils.delete_stack(self.heat_client, self.stack2)
134             except:
135                 pass
136
137         if self.image_creator:
138             try:
139                 self.image_creator.clean()
140             except:
141                 pass
142
143         if self.flavor_creator:
144             try:
145                 self.flavor_creator.clean()
146             except:
147                 pass
148
149         super(self.__class__, self).__clean__()
150
151     def test_create_stack(self):
152         """
153         Tests the creation of an OpenStack Heat stack1 that does not exist.
154         """
155         self.stack1 = heat_utils.create_stack(self.heat_client,
156                                               self.stack_settings1)
157
158         stack_query_1 = heat_utils.get_stack(
159             self.heat_client, stack_settings=self.stack_settings1)
160         self.assertEqual(self.stack1, stack_query_1)
161
162         stack_query_2 = heat_utils.get_stack(
163             self.heat_client, stack_name=self.stack_settings1.name)
164         self.assertEqual(self.stack1, stack_query_2)
165
166         stack_query_3 = heat_utils.get_stack_by_id(self.heat_client,
167                                                    self.stack1.id)
168         self.assertEqual(self.stack1, stack_query_3)
169
170         resources = heat_utils.get_resources(self.heat_client, self.stack1.id)
171         self.assertIsNotNone(resources)
172         self.assertEqual(4, len(resources))
173
174         outputs = heat_utils.get_outputs(self.heat_client, self.stack1)
175         self.assertIsNotNone(outputs)
176         self.assertEqual(0, len(outputs))
177
178         self.assertTrue(stack_active(self.heat_client, self.stack1))
179
180         neutron = neutron_utils.neutron_client(self.os_creds, self.os_session)
181         networks = heat_utils.get_stack_networks(
182             self.heat_client, neutron, self.stack1)
183         self.assertIsNotNone(networks)
184         self.assertEqual(1, len(networks))
185         self.assertEqual(self.network_name, networks[0].name)
186
187         subnets = neutron_utils.get_subnets_by_network(neutron, networks[0])
188         self.assertEqual(1, len(subnets))
189         self.assertEqual(self.subnet_name, subnets[0].name)
190
191         nova = nova_utils.nova_client(self.os_creds, self.os_session)
192         keystone = keystone_utils.keystone_client(
193             self.os_creds, self.os_session)
194         servers = heat_utils.get_stack_servers(
195             self.heat_client, nova, neutron, keystone, self.stack1,
196             self.os_creds.project_name)
197         self.assertIsNotNone(servers)
198         self.assertEqual(1, len(servers))
199         self.assertEqual(self.vm_inst_name, servers[0].name)
200
201     def test_create_stack_x2(self):
202         """
203         Tests the creation of an OpenStack keypair that does not exist.
204         """
205         self.stack1 = heat_utils.create_stack(self.heat_client,
206                                               self.stack_settings1)
207
208         stack1_query_1 = heat_utils.get_stack(
209             self.heat_client, stack_settings=self.stack_settings1)
210         self.assertEqual(self.stack1, stack1_query_1)
211
212         stack1_query_2 = heat_utils.get_stack(
213             self.heat_client, stack_name=self.stack_settings1.name)
214         self.assertEqual(self.stack1, stack1_query_2)
215
216         stack1_query_3 = heat_utils.get_stack_by_id(self.heat_client,
217                                                     self.stack1.id)
218         self.assertEqual(self.stack1, stack1_query_3)
219
220         self.assertTrue(stack_active(self.heat_client, self.stack1))
221
222         self.stack2 = heat_utils.create_stack(self.heat_client,
223                                               self.stack_settings2)
224
225         stack2_query_1 = heat_utils.get_stack(
226             self.heat_client, stack_settings=self.stack_settings2)
227         self.assertEqual(self.stack2, stack2_query_1)
228
229         stack2_query_2 = heat_utils.get_stack(
230             self.heat_client, stack_name=self.stack_settings2.name)
231         self.assertEqual(self.stack2, stack2_query_2)
232
233         stack2_query_3 = heat_utils.get_stack_by_id(self.heat_client,
234                                                     self.stack2.id)
235         self.assertEqual(self.stack2, stack2_query_3)
236
237         self.assertTrue(stack_active(self.heat_client, self.stack2))
238
239
240 class HeatUtilsCreateComplexStackTests(OSComponentTestCase):
241     """
242     Test basic Heat functionality
243     """
244
245     def setUp(self):
246         """
247         Instantiates OpenStack instances that cannot be spawned by Heat
248         """
249         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
250         stack_name = guid + '-stack'
251         self.network_name = guid + '-net'
252         self.subnet_name = guid + '-subnet'
253         self.vm_inst1_name = guid + '-inst1'
254         self.vm_inst2_name = guid + '-inst2'
255         self.flavor1_name = guid + '-flavor1'
256         self.flavor2_name = guid + '-flavor2'
257         self.keypair_name = guid + '-keypair'
258
259         self.image_creator1 = OpenStackImage(
260             self.os_creds, openstack_tests.cirros_image_settings(
261                 name=guid + '-image1', image_metadata=self.image_metadata))
262         self.image_creator1.create()
263
264         self.image_creator2 = OpenStackImage(
265             self.os_creds, openstack_tests.cirros_image_settings(
266                 name=guid + '-image2', image_metadata=self.image_metadata))
267         self.image_creator2.create()
268
269         env_values = {'image1_name': self.image_creator1.image_settings.name,
270                       'image2_name': self.image_creator2.image_settings.name,
271                       'flavor1_name': self.flavor1_name,
272                       'flavor2_name': self.flavor2_name,
273                       'net_name': self.network_name,
274                       'subnet_name': self.subnet_name,
275                       'keypair_name': self.keypair_name,
276                       'inst1_name': self.vm_inst1_name,
277                       'inst2_name': self.vm_inst2_name,
278                       'external_net_name': self.ext_net_name}
279         heat_tmplt_path = pkg_resources.resource_filename(
280             'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
281         stack_settings = StackConfig(
282             name=stack_name, template_path=heat_tmplt_path,
283             env_values=env_values)
284         self.heat_client = heat_utils.heat_client(
285             self.os_creds, self.os_session)
286         self.stack = heat_utils.create_stack(self.heat_client, stack_settings)
287
288         self.assertTrue(stack_active(self.heat_client, self.stack))
289
290         self.keypair1_settings = None
291         self.keypair2_settings = None
292
293     def tearDown(self):
294         """
295         Cleans the stack and image
296         """
297         if self.stack:
298             try:
299                 heat_utils.delete_stack(self.heat_client, self.stack)
300                 # Wait until stack deployment has completed
301                 end_time = (time.time() +
302                             stack_config.STACK_COMPLETE_TIMEOUT)
303                 is_deleted = False
304                 while time.time() < end_time:
305                     status = heat_utils.get_stack_status(self.heat_client,
306                                                          self.stack.id)
307                     if status == stack_config.STATUS_DELETE_COMPLETE:
308                         is_deleted = True
309                         break
310                     elif status == stack_config.STATUS_DELETE_FAILED:
311                         is_deleted = False
312                         break
313
314                     time.sleep(3)
315
316                 if not is_deleted:
317                     nova = nova_utils.nova_client(
318                         self.os_creds, self.os_session)
319                     keystone = keystone_utils.keystone_client(
320                         self.os_creds, self.os_session)
321                     neutron = neutron_utils.neutron_client(
322                         self.os_creds, self.os_session)
323                     glance = glance_utils.glance_client(
324                         self.os_creds, self.os_session)
325
326                     servers = heat_utils.get_stack_servers(
327                         self.heat_client, nova, neutron, keystone, self.stack,
328                         self.os_creds.project_name)
329                     for server in servers:
330                         vm_settings = settings_utils.create_vm_inst_config(
331                             nova, keystone, neutron, server,
332                             self.os_creds.project_name)
333                         img_settings = settings_utils.determine_image_config(
334                             glance, server,
335                             [self.image_creator1.image_settings,
336                              self.image_creator2.image_settings])
337                         vm_creator = OpenStackVmInstance(
338                             self.os_creds, vm_settings, img_settings)
339                         vm_creator.initialize()
340                         vm_creator.clean()
341                         vm_creator.vm_deleted(block=True)
342
343                     heat_utils.delete_stack(self.heat_client, self.stack)
344                     time.sleep(20)
345             except:
346                     raise
347
348         if self.image_creator1:
349             try:
350                 self.image_creator1.clean()
351             except:
352                 pass
353
354         if self.image_creator2:
355             try:
356                 self.image_creator2.clean()
357             except:
358                 pass
359
360         if self.keypair1_settings:
361             expanded_path = os.path.expanduser(
362                 self.keypair1_settings.private_filepath)
363             os.chmod(expanded_path, 0o755)
364             os.remove(expanded_path)
365
366         if self.keypair2_settings:
367             expanded_path = os.path.expanduser(
368                 self.keypair2_settings.private_filepath)
369             os.chmod(expanded_path, 0o755)
370             os.remove(expanded_path)
371
372         super(self.__class__, self).__clean__()
373
374     def test_get_settings_from_stack(self):
375         """
376         Tests that a heat template with floating IPs and can have the proper
377         settings derived from settings_utils.py.
378         """
379         resources = heat_utils.get_resources(self.heat_client, self.stack.id)
380         self.assertIsNotNone(resources)
381         self.assertEqual(12, len(resources))
382
383         options = heat_utils.get_outputs(self.heat_client, self.stack)
384         self.assertIsNotNone(options)
385         self.assertEqual(1, len(options))
386
387         neutron = neutron_utils.neutron_client(self.os_creds, self.os_session)
388         networks = heat_utils.get_stack_networks(
389             self.heat_client, neutron, self.stack)
390         self.assertIsNotNone(networks)
391         self.assertEqual(1, len(networks))
392         self.assertEqual(self.network_name, networks[0].name)
393
394         network_settings = settings_utils.create_network_config(
395             neutron, networks[0])
396         self.assertIsNotNone(network_settings)
397         self.assertEqual(self.network_name, network_settings.name)
398
399         nova = nova_utils.nova_client(self.os_creds, self.os_session)
400         glance = glance_utils.glance_client(self.os_creds, self.os_session)
401         keystone = keystone_utils.keystone_client(
402             self.os_creds, self.os_session)
403         servers = heat_utils.get_stack_servers(
404             self.heat_client, nova, neutron, keystone, self.stack,
405             self.os_creds.project_name)
406         self.assertIsNotNone(servers)
407         self.assertEqual(2, len(servers))
408
409         image_settings = settings_utils.determine_image_config(
410             glance, servers[0],
411             [self.image_creator1.image_settings,
412              self.image_creator2.image_settings])
413
414         self.assertIsNotNone(image_settings)
415         if image_settings.name.endswith('1'):
416             self.assertEqual(
417                 self.image_creator1.image_settings.name, image_settings.name)
418         else:
419             self.assertEqual(
420                 self.image_creator2.image_settings.name, image_settings.name)
421
422         image_settings = settings_utils.determine_image_config(
423             glance, servers[1],
424             [self.image_creator1.image_settings,
425              self.image_creator2.image_settings])
426         if image_settings.name.endswith('1'):
427             self.assertEqual(
428                 self.image_creator1.image_settings.name, image_settings.name)
429         else:
430             self.assertEqual(
431                 self.image_creator2.image_settings.name, image_settings.name)
432
433         self.keypair1_settings = settings_utils.determine_keypair_config(
434             self.heat_client, self.stack, servers[0],
435             priv_key_key='private_key')
436         self.assertIsNotNone(self.keypair1_settings)
437         self.assertEqual(self.keypair_name, self.keypair1_settings.name)
438
439         self.keypair2_settings = settings_utils.determine_keypair_config(
440             self.heat_client, self.stack, servers[1],
441             priv_key_key='private_key')
442         self.assertIsNotNone(self.keypair2_settings)
443         self.assertEqual(self.keypair_name, self.keypair2_settings.name)
444
445
446 class HeatUtilsRouterTests(OSComponentTestCase):
447     """
448     Test Heat volume functionality
449     """
450
451     def setUp(self):
452         """
453         Instantiates OpenStack instances that cannot be spawned by Heat
454         """
455         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
456         stack_name = guid + '-stack'
457
458         self.net_name = guid + '-net'
459         self.subnet_name = guid + '-subnet'
460         self.router_name = guid + '-router'
461
462         env_values = {
463             'net_name': self.net_name,
464             'subnet_name': self.subnet_name,
465             'router_name': self.router_name,
466             'external_net_name': self.ext_net_name}
467
468         heat_tmplt_path = pkg_resources.resource_filename(
469             'snaps.openstack.tests.heat', 'router_heat_template.yaml')
470         self.stack_settings = StackConfig(
471             name=stack_name, template_path=heat_tmplt_path,
472             env_values=env_values)
473         self.stack = None
474         self.heat_client = heat_utils.heat_client(
475             self.os_creds, self.os_session)
476         self.neutron = neutron_utils.neutron_client(
477             self.os_creds, self.os_session)
478
479     def tearDown(self):
480         """
481         Cleans the image and downloaded image file
482         """
483         if self.stack:
484             try:
485                 heat_utils.delete_stack(self.heat_client, self.stack)
486             except:
487                 pass
488
489         super(self.__class__, self).__clean__()
490
491     def test_create_router_with_stack(self):
492         """
493         Tests the creation of an OpenStack router with Heat and the retrieval
494         of the Router Domain objects from heat_utils#get_stack_routers().
495         """
496         self.stack = heat_utils.create_stack(
497             self.heat_client, self.stack_settings)
498
499         # Wait until stack deployment has completed
500         end_time = time.time() + stack_config.STACK_COMPLETE_TIMEOUT
501         is_active = False
502         while time.time() < end_time:
503             status = heat_utils.get_stack_status(self.heat_client,
504                                                  self.stack.id)
505             if status == stack_config.STATUS_CREATE_COMPLETE:
506                 is_active = True
507                 break
508             elif status == stack_config.STATUS_CREATE_FAILED:
509                 is_active = False
510                 break
511
512             time.sleep(3)
513
514         self.assertTrue(is_active)
515
516         routers = heat_utils.get_stack_routers(
517             self.heat_client, self.neutron, self.stack)
518
519         self.assertEqual(1, len(routers))
520
521         router = routers[0]
522         self.assertEqual(self.router_name, router.name)
523
524         keystone = keystone_utils.keystone_client(
525             self.os_creds, self.os_session)
526         ext_net = neutron_utils.get_network(
527             self.neutron, keystone, network_name=self.ext_net_name)
528         self.assertEqual(ext_net.id, router.external_network_id)
529
530
531 class HeatUtilsVolumeTests(OSComponentTestCase):
532     """
533     Test Heat volume functionality
534     """
535
536     def setUp(self):
537         """
538         Instantiates OpenStack instances that cannot be spawned by Heat
539         """
540         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
541         stack_name = guid + '-stack'
542         self.volume_name = guid + '-vol'
543         self.volume_type_name = guid + '-vol-type'
544
545         env_values = {
546             'volume_name': self.volume_name,
547             'volume_type_name': self.volume_type_name}
548
549         heat_tmplt_path = pkg_resources.resource_filename(
550             'snaps.openstack.tests.heat', 'volume_heat_template.yaml')
551         self.stack_settings = StackConfig(
552             name=stack_name, template_path=heat_tmplt_path,
553             env_values=env_values)
554         self.stack = None
555         self.heat_client = heat_utils.heat_client(
556             self.os_creds, self.os_session)
557         self.cinder = cinder_utils.cinder_client(
558             self.os_creds, self.os_session)
559
560     def tearDown(self):
561         """
562         Cleans the stack
563         """
564         if self.stack:
565             try:
566                 heat_utils.delete_stack(self.heat_client, self.stack)
567             except:
568                 pass
569
570         super(self.__class__, self).__clean__()
571
572     def test_create_vol_with_stack(self):
573         """
574         Tests the creation of an OpenStack volume with Heat.
575         """
576         self.stack = heat_utils.create_stack(
577             self.heat_client, self.stack_settings)
578         self.assertTrue(stack_active(self.heat_client, self.stack))
579
580         volumes = heat_utils.get_stack_volumes(
581             self.heat_client, self.cinder, self.stack)
582
583         self.assertEqual(1, len(volumes))
584
585         volume = volumes[0]
586         self.assertEqual(self.volume_name, volume.name)
587         self.assertEqual(self.volume_type_name, volume.type)
588         self.assertEqual(1, volume.size)
589         self.assertEqual(False, volume.multi_attach)
590
591     def test_create_vol_types_with_stack(self):
592         """
593         Tests the creation of an OpenStack volume with Heat.
594         """
595         self.stack = heat_utils.create_stack(
596             self.heat_client, self.stack_settings)
597         self.assertTrue(stack_active(self.heat_client, self.stack))
598
599         volume_types = heat_utils.get_stack_volume_types(
600             self.heat_client, self.cinder, self.stack)
601
602         self.assertEqual(1, len(volume_types))
603
604         volume_type = volume_types[0]
605
606         self.assertEqual(self.volume_type_name, volume_type.name)
607         self.assertTrue(volume_type.public)
608         self.assertIsNone(volume_type.qos_spec)
609
610         # TODO - Add encryption back and find out why it broke in Pike
611         # encryption = volume_type.encryption
612         # self.assertIsNotNone(encryption)
613         # self.assertIsNone(encryption.cipher)
614         # self.assertEqual('front-end', encryption.control_location)
615         # self.assertIsNone(encryption.key_size)
616         # self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor',
617         #                  encryption.provider)
618         # self.assertEqual(volume_type.id, encryption.volume_type_id)
619
620
621 class HeatUtilsFlavorTests(OSComponentTestCase):
622     """
623     Test Heat volume functionality
624     """
625
626     def setUp(self):
627         """
628         Instantiates OpenStack instances that cannot be spawned by Heat
629         """
630         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
631         self.name_prefix = guid
632         stack_name = guid + '-stack'
633
634         heat_tmplt_path = pkg_resources.resource_filename(
635             'snaps.openstack.tests.heat', 'flavor_heat_template.yaml')
636         self.stack_settings = StackConfig(
637             name=stack_name, template_path=heat_tmplt_path)
638         self.stack = None
639         self.heat_client = heat_utils.heat_client(
640             self.os_creds, self.os_session)
641         self.nova = nova_utils.nova_client(
642             self.os_creds, self.os_session)
643
644     def tearDown(self):
645         """
646         Cleans the stack
647         """
648         if self.stack:
649             try:
650                 heat_utils.delete_stack(self.heat_client, self.stack)
651             except:
652                 pass
653
654         super(self.__class__, self).__clean__()
655
656     def test_create_flavor_with_stack(self):
657         """
658         Tests the creation of an OpenStack volume with Heat.
659         """
660         self.stack = heat_utils.create_stack(
661             self.heat_client, self.stack_settings)
662
663         self.assertTrue(stack_active(self.heat_client, self.stack))
664
665         flavors = heat_utils.get_stack_flavors(
666             self.heat_client, self.nova, self.stack)
667
668         self.assertEqual(1, len(flavors))
669
670         flavor = flavors[0]
671         self.assertTrue(flavor.name.startswith(self.name_prefix))
672         self.assertEqual(1024, flavor.ram)
673         self.assertEqual(200, flavor.disk)
674         self.assertEqual(8, flavor.vcpus)
675         self.assertEqual(0, flavor.ephemeral)
676         self.assertIsNone(flavor.swap)
677         self.assertEqual(1.0, flavor.rxtx_factor)
678         self.assertTrue(flavor.is_public)
679
680
681 class HeatUtilsKeypairTests(OSComponentTestCase):
682     """
683     Test Heat volume functionality
684     """
685
686     def setUp(self):
687         """
688         Instantiates OpenStack instances that cannot be spawned by Heat
689         """
690         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
691         stack_name = guid + '-stack'
692         self.keypair_name = guid + '-kp'
693
694         env_values = {'keypair_name': self.keypair_name}
695
696         heat_tmplt_path = pkg_resources.resource_filename(
697             'snaps.openstack.tests.heat', 'keypair_heat_template.yaml')
698         self.stack_settings = StackConfig(
699             name=stack_name, template_path=heat_tmplt_path,
700             env_values=env_values)
701         self.stack = None
702         self.heat_client = heat_utils.heat_client(
703             self.os_creds, self.os_session)
704         self.nova = nova_utils.nova_client(
705             self.os_creds, self.os_session)
706
707     def tearDown(self):
708         """
709         Cleans the stack
710         """
711         if self.stack:
712             try:
713                 heat_utils.delete_stack(self.heat_client, self.stack)
714             except:
715                 pass
716
717         super(self.__class__, self).__clean__()
718
719     def test_create_keypair_with_stack(self):
720         """
721         Tests the creation of an OpenStack keypair with Heat.
722         """
723         self.stack = heat_utils.create_stack(
724             self.heat_client, self.stack_settings)
725         self.assertTrue(stack_active(self.heat_client, self.stack))
726
727         keypairs = heat_utils.get_stack_keypairs(
728             self.heat_client, self.nova, self.stack)
729
730         self.assertEqual(1, len(keypairs))
731         keypair = keypairs[0]
732
733         self.assertEqual(self.keypair_name, keypair.name)
734
735         outputs = heat_utils.get_outputs(self.heat_client, self.stack)
736
737         for output in outputs:
738             if output.key == 'private_key':
739                 self.assertTrue(output.value.startswith(
740                     '-----BEGIN RSA PRIVATE KEY-----'))
741
742         keypair = nova_utils.get_keypair_by_id(self.nova, keypair.id)
743         self.assertIsNotNone(keypair)
744
745         self.assertEqual(self.keypair_name, keypair.name)
746
747
748 class HeatUtilsSecurityGroupTests(OSComponentTestCase):
749     """
750     Test Heat volume functionality
751     """
752
753     def setUp(self):
754         """
755         Instantiates OpenStack instances that cannot be spawned by Heat
756         """
757         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
758         stack_name = guid + '-stack'
759         self.sec_grp_name = guid + '-sec-grp'
760
761         env_values = {'security_group_name': self.sec_grp_name}
762
763         heat_tmplt_path = pkg_resources.resource_filename(
764             'snaps.openstack.tests.heat', 'security_group_heat_template.yaml')
765         self.stack_settings = StackConfig(
766             name=stack_name, template_path=heat_tmplt_path,
767             env_values=env_values)
768         self.stack = None
769         self.heat_client = heat_utils.heat_client(
770             self.os_creds, self.os_session)
771         self.neutron = neutron_utils.neutron_client(
772             self.os_creds, self.os_session)
773
774     def tearDown(self):
775         """
776         Cleans the stack
777         """
778         if self.stack:
779             try:
780                 heat_utils.delete_stack(self.heat_client, self.stack)
781             except:
782                 pass
783
784         super(self.__class__, self).__clean__()
785
786     def test_create_security_group_with_stack(self):
787         """
788         Tests the creation of an OpenStack SecurityGroup with Heat.
789         """
790         self.stack = heat_utils.create_stack(
791             self.heat_client, self.stack_settings)
792         self.assertTrue(stack_active(self.heat_client, self.stack))
793
794         sec_grp = heat_utils.get_stack_security_groups(
795             self.heat_client, self.neutron, self.stack)[0]
796
797         self.assertEqual(self.sec_grp_name, sec_grp.name)
798         self.assertEqual('Test description', sec_grp.description)
799         self.assertEqual(2, len(sec_grp.rules))
800
801         has_ssh_rule = False
802         has_icmp_rule = False
803
804         for rule in sec_grp.rules:
805             if (rule.security_group_id == sec_grp.id
806                     and rule.direction == 'egress'
807                     and rule.ethertype == 'IPv4'
808                     and rule.port_range_min == 22
809                     and rule.port_range_max == 22
810                     and rule.protocol == 'tcp'
811                     and rule.remote_group_id is None
812                     and rule.remote_ip_prefix == '0.0.0.0/0'):
813                 has_ssh_rule = True
814             if (rule.security_group_id == sec_grp.id
815                     and rule.direction == 'ingress'
816                     and rule.ethertype == 'IPv4'
817                     and rule.port_range_min is None
818                     and rule.port_range_max is None
819                     and rule.protocol == 'icmp'
820                     and rule.remote_group_id is None
821                     and rule.remote_ip_prefix == '0.0.0.0/0'):
822                 has_icmp_rule = True
823
824         self.assertTrue(has_ssh_rule)
825         self.assertTrue(has_icmp_rule)
826
827
828 def stack_active(heat_cli, stack):
829     """
830     Blocks until stack application has successfully completed or failed
831     :param heat_cli: the Heat client
832     :param stack: the Stack domain object
833     :return: T/F
834     """
835     # Wait until stack deployment has completed
836     end_time = time.time() + stack_config.STACK_COMPLETE_TIMEOUT
837     is_active = False
838     while time.time() < end_time:
839         status = heat_utils.get_stack_status(heat_cli, stack.id)
840         if status == stack_config.STATUS_CREATE_COMPLETE:
841             is_active = True
842             break
843         elif status == stack_config.STATUS_CREATE_FAILED:
844             is_active = False
845             break
846
847         time.sleep(3)
848
849     return is_active