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