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