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