Fixed vm instance instantiation from Heat when using nested resources
[snaps.git] / snaps / openstack / tests / create_stack_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 os
16 import time
17
18 import pkg_resources
19 from heatclient.exc import HTTPBadRequest
20
21 import snaps
22 from snaps import file_utils
23 from snaps.config.flavor import FlavorConfig
24 from snaps.config.image import ImageConfig
25 from snaps.config.stack import StackConfigError, StackConfig
26 from snaps.openstack.create_flavor import OpenStackFlavor
27 from snaps.openstack.create_image import OpenStackImage
28
29 try:
30     from urllib.request import URLError
31 except ImportError:
32     from urllib2 import URLError
33
34 import logging
35 import unittest
36 import uuid
37
38 from snaps.openstack.create_stack import (
39     StackSettings, StackCreationError, StackError, OpenStackHeatStack)
40 from snaps.openstack.tests import openstack_tests, create_instance_tests
41 from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
42 from snaps.openstack.utils import heat_utils, neutron_utils, nova_utils
43
44 __author__ = 'spisarski'
45
46 logger = logging.getLogger('create_stack_tests')
47
48
49 class StackSettingsUnitTests(unittest.TestCase):
50     """
51     Tests the construction of the StackSettings class
52     """
53
54     def test_no_params(self):
55         with self.assertRaises(StackConfigError):
56             StackSettings()
57
58     def test_empty_config(self):
59         with self.assertRaises(StackConfigError):
60             StackSettings(**dict())
61
62     def test_name_only(self):
63         with self.assertRaises(StackConfigError):
64             StackSettings(name='foo')
65
66     def test_config_with_name_only(self):
67         with self.assertRaises(StackConfigError):
68             StackSettings(**{'name': 'foo'})
69
70     def test_config_minimum_template(self):
71         settings = StackSettings(**{'name': 'stack', 'template': 'foo'})
72         self.assertEqual('stack', settings.name)
73         self.assertEqual('foo', settings.template)
74         self.assertIsNone(settings.template_path)
75         self.assertIsNone(settings.env_values)
76         self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
77                          settings.stack_create_timeout)
78
79     def test_config_minimum_template_path(self):
80         settings = StackSettings(**{'name': 'stack', 'template_path': 'foo'})
81         self.assertEqual('stack', settings.name)
82         self.assertIsNone(settings.template)
83         self.assertEqual('foo', settings.template_path)
84         self.assertIsNone(settings.env_values)
85         self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
86                          settings.stack_create_timeout)
87
88     def test_minimum_template(self):
89         settings = StackSettings(name='stack', template='foo')
90         self.assertEqual('stack', settings.name)
91         self.assertEqual('foo', settings.template)
92         self.assertIsNone(settings.template_path)
93         self.assertIsNone(settings.env_values)
94         self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
95                          settings.stack_create_timeout)
96
97     def test_minimum_template_path(self):
98         settings = StackSettings(name='stack', template_path='foo')
99         self.assertEqual('stack', settings.name)
100         self.assertEqual('foo', settings.template_path)
101         self.assertIsNone(settings.template)
102         self.assertIsNone(settings.env_values)
103         self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
104                          settings.stack_create_timeout)
105
106     def test_all(self):
107         env_values = {'foo': 'bar'}
108         settings = StackSettings(name='stack', template='bar',
109                                  template_path='foo', env_values=env_values,
110                                  stack_create_timeout=999)
111         self.assertEqual('stack', settings.name)
112         self.assertEqual('bar', settings.template)
113         self.assertEqual('foo', settings.template_path)
114         self.assertEqual(env_values, settings.env_values)
115         self.assertEqual(999, settings.stack_create_timeout)
116
117     def test_config_all(self):
118         env_values = {'foo': 'bar'}
119         settings = StackSettings(
120             **{'name': 'stack', 'template': 'bar', 'template_path': 'foo',
121                'env_values': env_values, 'stack_create_timeout': 999})
122         self.assertEqual('stack', settings.name)
123         self.assertEqual('bar', settings.template)
124         self.assertEqual('foo', settings.template_path)
125         self.assertEqual(env_values, settings.env_values)
126         self.assertEqual(999, settings.stack_create_timeout)
127
128
129 class CreateStackSuccessTests(OSIntegrationTestCase):
130     """
131     Tests for the OpenStackHeatStack class defined in create_stack.py
132     """
133
134     def setUp(self):
135
136         super(self.__class__, self).__start__()
137
138         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
139
140         self.heat_creds = self.admin_os_creds
141         self.heat_creds.project_name = self.admin_os_creds.project_name
142
143         self.heat_cli = heat_utils.heat_client(self.heat_creds)
144         self.stack_creator = None
145
146         self.image_creator = OpenStackImage(
147             self.heat_creds, openstack_tests.cirros_image_settings(
148                 name=self.guid + '-image',
149                 image_metadata=self.image_metadata))
150         self.image_creator.create()
151
152         # Create Flavor
153         self.flavor_creator = OpenStackFlavor(
154             self.admin_os_creds,
155             FlavorConfig(
156                 name=self.guid + '-flavor-name', ram=256, disk=10, vcpus=1))
157         self.flavor_creator.create()
158
159         self.network_name = self.guid + '-net'
160         self.subnet_name = self.guid + '-subnet'
161         self.vm_inst_name = self.guid + '-inst'
162
163         self.env_values = {
164             'image_name': self.image_creator.image_settings.name,
165             'flavor_name': self.flavor_creator.flavor_settings.name,
166             'net_name': self.network_name,
167             'subnet_name': self.subnet_name,
168             'inst_name': self.vm_inst_name}
169
170         self.heat_tmplt_path = pkg_resources.resource_filename(
171             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
172
173     def tearDown(self):
174         """
175         Cleans the stack and downloaded stack file
176         """
177         if self.stack_creator:
178             try:
179                 self.stack_creator.clean()
180             except:
181                 pass
182
183         if self.image_creator:
184             try:
185                 self.image_creator.clean()
186             except:
187                 pass
188
189         if self.flavor_creator:
190             try:
191                 self.flavor_creator.clean()
192             except:
193                 pass
194
195         super(self.__class__, self).__clean__()
196
197     def test_create_stack_template_file(self):
198         """
199         Tests the creation of an OpenStack stack from Heat template file.
200         """
201         # Create Stack
202         # Set the default stack settings, then set any custom parameters sent
203         # from the app
204         stack_settings = StackConfig(
205             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
206             template_path=self.heat_tmplt_path,
207             env_values=self.env_values)
208         self.stack_creator = OpenStackHeatStack(
209             self.heat_creds, stack_settings)
210         created_stack = self.stack_creator.create()
211         self.assertIsNotNone(created_stack)
212
213         retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
214                                                      created_stack.id)
215         self.assertIsNotNone(retrieved_stack)
216         self.assertEqual(created_stack.name, retrieved_stack.name)
217         self.assertEqual(created_stack.id, retrieved_stack.id)
218         self.assertEqual(0, len(self.stack_creator.get_outputs()))
219
220     def test_create_stack_short_timeout(self):
221         """
222         Tests the creation of an OpenStack stack from Heat template file.
223         """
224         # Create Stack
225         # Set the default stack settings, then set any custom parameters sent
226         # from the app
227         stack_settings = StackConfig(
228             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
229             template_path=self.heat_tmplt_path,
230             env_values=self.env_values, stack_create_timeout=0)
231
232         self.stack_creator = OpenStackHeatStack(
233             self.heat_creds, stack_settings)
234         with self.assertRaises(StackCreationError):
235             self.stack_creator.create()
236
237     def test_create_stack_template_dict(self):
238         """
239         Tests the creation of an OpenStack stack from a heat dict() object.
240         """
241         # Create Stack
242         # Set the default stack settings, then set any custom parameters sent
243         # from the app
244         template_dict = heat_utils.parse_heat_template_str(
245             file_utils.read_file(self.heat_tmplt_path))
246         stack_settings = StackConfig(
247             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
248             template=template_dict,
249             env_values=self.env_values)
250         self.stack_creator = OpenStackHeatStack(
251             self.heat_creds, stack_settings)
252         created_stack = self.stack_creator.create()
253         self.assertIsNotNone(created_stack)
254
255         retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
256                                                      created_stack.id)
257         self.assertIsNotNone(retrieved_stack)
258         self.assertEqual(created_stack.name, retrieved_stack.name)
259         self.assertEqual(created_stack.id, retrieved_stack.id)
260         self.assertEqual(0, len(self.stack_creator.get_outputs()))
261
262     def test_create_delete_stack(self):
263         """
264         Tests the creation then deletion of an OpenStack stack to ensure
265         clean() does not raise an Exception.
266         """
267         # Create Stack
268         template_dict = heat_utils.parse_heat_template_str(
269             file_utils.read_file(self.heat_tmplt_path))
270         stack_settings = StackConfig(
271             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
272             template=template_dict,
273             env_values=self.env_values)
274         self.stack_creator = OpenStackHeatStack(
275             self.heat_creds, stack_settings)
276         created_stack = self.stack_creator.create()
277         self.assertIsNotNone(created_stack)
278
279         retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
280                                                      created_stack.id)
281         self.assertIsNotNone(retrieved_stack)
282         self.assertEqual(created_stack.name, retrieved_stack.name)
283         self.assertEqual(created_stack.id, retrieved_stack.id)
284         self.assertEqual(0, len(self.stack_creator.get_outputs()))
285         self.assertEqual(snaps.config.stack.STATUS_CREATE_COMPLETE,
286                          self.stack_creator.get_status())
287
288         # Delete Stack manually
289         heat_utils.delete_stack(self.heat_cli, created_stack)
290
291         end_time = time.time() + 90
292         deleted = False
293         while time.time() < end_time:
294             status = heat_utils.get_stack_status(self.heat_cli,
295                                                  retrieved_stack.id)
296             if status == snaps.config.stack.STATUS_DELETE_COMPLETE:
297                 deleted = True
298                 break
299
300         self.assertTrue(deleted)
301
302         # Must not throw an exception when attempting to cleanup non-existent
303         # stack
304         self.stack_creator.clean()
305         self.assertIsNone(self.stack_creator.get_stack())
306
307     def test_create_same_stack(self):
308         """
309         Tests the creation of an OpenStack stack when the stack already exists.
310         """
311         # Create Stack
312         template_dict = heat_utils.parse_heat_template_str(
313             file_utils.read_file(self.heat_tmplt_path))
314         stack_settings = StackConfig(
315             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
316             template=template_dict,
317             env_values=self.env_values)
318         self.stack_creator = OpenStackHeatStack(
319             self.heat_creds, stack_settings)
320         created_stack1 = self.stack_creator.create()
321
322         retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
323                                                      created_stack1.id)
324         self.assertIsNotNone(retrieved_stack)
325         self.assertEqual(created_stack1.name, retrieved_stack.name)
326         self.assertEqual(created_stack1.id, retrieved_stack.id)
327         self.assertEqual(0, len(self.stack_creator.get_outputs()))
328
329         # Should be retrieving the instance data
330         stack_creator2 = OpenStackHeatStack(self.heat_creds, stack_settings)
331         stack2 = stack_creator2.create()
332         self.assertEqual(created_stack1.id, stack2.id)
333
334     def test_retrieve_network_creators(self):
335         """
336         Tests the creation of an OpenStack stack from Heat template file and
337         the retrieval of the network creator.
338         """
339         stack_settings = StackConfig(
340             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
341             template_path=self.heat_tmplt_path,
342             env_values=self.env_values)
343         self.stack_creator = OpenStackHeatStack(
344             self.heat_creds, stack_settings)
345         created_stack = self.stack_creator.create()
346         self.assertIsNotNone(created_stack)
347
348         net_creators = self.stack_creator.get_network_creators()
349         self.assertIsNotNone(net_creators)
350         self.assertEqual(1, len(net_creators))
351         self.assertEqual(self.network_name, net_creators[0].get_network().name)
352
353         neutron = neutron_utils.neutron_client(self.os_creds)
354         net_by_name = neutron_utils.get_network(
355             neutron, network_name=net_creators[0].get_network().name)
356         self.assertEqual(net_creators[0].get_network(), net_by_name)
357         self.assertIsNotNone(neutron_utils.get_network_by_id(
358             neutron, net_creators[0].get_network().id))
359
360         self.assertEqual(1, len(net_creators[0].get_network().subnets))
361         subnet = net_creators[0].get_network().subnets[0]
362         subnet_by_name = neutron_utils.get_subnet(
363             neutron, subnet_name=subnet.name)
364         self.assertEqual(subnet, subnet_by_name)
365
366         subnet_by_id = neutron_utils.get_subnet_by_id(neutron, subnet.id)
367         self.assertIsNotNone(subnet_by_id)
368         self.assertEqual(subnet_by_name, subnet_by_id)
369
370     def test_retrieve_vm_inst_creators(self):
371         """
372         Tests the creation of an OpenStack stack from Heat template file and
373         the retrieval of the network creator.
374         """
375         stack_settings = StackConfig(
376             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
377             template_path=self.heat_tmplt_path,
378             env_values=self.env_values)
379         self.stack_creator = OpenStackHeatStack(
380             self.heat_creds, stack_settings)
381         created_stack = self.stack_creator.create()
382         self.assertIsNotNone(created_stack)
383
384         vm_inst_creators = self.stack_creator.get_vm_inst_creators()
385         self.assertIsNotNone(vm_inst_creators)
386         self.assertEqual(1, len(vm_inst_creators))
387         self.assertEqual(self.vm_inst_name,
388                          vm_inst_creators[0].get_vm_inst().name)
389
390         nova = nova_utils.nova_client(self.admin_os_creds)
391         neutron = neutron_utils.neutron_client(self.admin_os_creds)
392         vm_inst_by_name = nova_utils.get_server(
393             nova, neutron, server_name=vm_inst_creators[0].get_vm_inst().name)
394         self.assertEqual(vm_inst_creators[0].get_vm_inst(), vm_inst_by_name)
395         self.assertIsNotNone(nova_utils.get_server_object_by_id(
396             nova, neutron, vm_inst_creators[0].get_vm_inst().id))
397
398
399 class CreateStackFloatingIpTests(OSIntegrationTestCase):
400     """
401     Tests to ensure that floating IPs can be accessed via an
402     OpenStackVmInstance object obtained from the OpenStackHeatStack instance
403     """
404
405     def setUp(self):
406
407         super(self.__class__, self).__start__()
408
409         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
410
411         self.heat_creds = self.admin_os_creds
412         self.heat_creds.project_name = self.admin_os_creds.project_name
413
414         self.heat_cli = heat_utils.heat_client(self.heat_creds)
415         self.stack_creator = None
416
417         self.image_creator = OpenStackImage(
418             self.heat_creds, openstack_tests.cirros_image_settings(
419                 name=self.guid + '-image',
420                 image_metadata=self.image_metadata))
421         self.image_creator.create()
422
423         self.network_name = self.guid + '-net'
424         self.subnet_name = self.guid + '-subnet'
425         self.flavor1_name = self.guid + '-flavor1'
426         self.flavor2_name = self.guid + '-flavor2'
427         self.sec_grp_name = self.guid + '-sec_grp'
428         self.vm_inst1_name = self.guid + '-inst1'
429         self.vm_inst2_name = self.guid + '-inst2'
430         self.keypair_name = self.guid + '-kp'
431
432         self.env_values = {
433             'image1_name': self.image_creator.image_settings.name,
434             'image2_name': self.image_creator.image_settings.name,
435             'flavor1_name': self.flavor1_name,
436             'flavor2_name': self.flavor2_name,
437             'net_name': self.network_name,
438             'subnet_name': self.subnet_name,
439             'inst1_name': self.vm_inst1_name,
440             'inst2_name': self.vm_inst2_name,
441             'keypair_name': self.keypair_name,
442             'external_net_name': self.ext_net_name,
443             'security_group_name': self.sec_grp_name}
444
445         self.heat_tmplt_path = pkg_resources.resource_filename(
446             'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
447
448         self.vm_inst_creators = list()
449
450     def tearDown(self):
451         """
452         Cleans the stack and downloaded stack file
453         """
454         if self.stack_creator:
455             try:
456                 self.stack_creator.clean()
457             except:
458                 pass
459
460         if self.image_creator:
461             try:
462                 self.image_creator.clean()
463             except:
464                 pass
465
466         for vm_inst_creator in self.vm_inst_creators:
467             try:
468                 keypair_settings = vm_inst_creator.keypair_settings
469                 if keypair_settings and keypair_settings.private_filepath:
470                     expanded_path = os.path.expanduser(
471                         keypair_settings.private_filepath)
472                     os.chmod(expanded_path, 0o755)
473                     os.remove(expanded_path)
474             except:
475                 pass
476
477         super(self.__class__, self).__clean__()
478
479     def test_connect_via_ssh_heat_vm(self):
480         """
481         Tests the creation of an OpenStack stack from Heat template file and
482         the retrieval of two VM instance creators and attempt to connect via
483         SSH to the first one with a floating IP.
484         """
485         stack_settings = StackConfig(
486             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
487             template_path=self.heat_tmplt_path,
488             env_values=self.env_values)
489         self.stack_creator = OpenStackHeatStack(
490             self.heat_creds, stack_settings,
491             [self.image_creator.image_settings])
492         created_stack = self.stack_creator.create()
493         self.assertIsNotNone(created_stack)
494
495         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
496             heat_keypair_option='private_key')
497         self.assertIsNotNone(self.vm_inst_creators)
498         self.assertEqual(2, len(self.vm_inst_creators))
499
500         for vm_inst_creator in self.vm_inst_creators:
501             if vm_inst_creator.get_vm_inst().name == self.vm_inst1_name:
502                 self.assertTrue(
503                     create_instance_tests.validate_ssh_client(vm_inst_creator))
504             else:
505                 vm_settings = vm_inst_creator.instance_settings
506                 self.assertEqual(0, len(vm_settings.floating_ip_settings))
507
508
509 class CreateStackNestedResourceTests(OSIntegrationTestCase):
510     """
511     Tests to ensure that nested heat templates work
512     """
513
514     def setUp(self):
515
516         super(self.__class__, self).__start__()
517
518         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
519
520         self.heat_creds = self.admin_os_creds
521         self.heat_creds.project_name = self.admin_os_creds.project_name
522
523         self.heat_cli = heat_utils.heat_client(self.heat_creds)
524         self.stack_creator = None
525
526         self.image_creator = OpenStackImage(
527             self.heat_creds, openstack_tests.cirros_image_settings(
528                 name=self.guid + '-image',
529                 image_metadata=self.image_metadata))
530         self.image_creator.create()
531
532         self.flavor_creator = OpenStackFlavor(
533             self.admin_os_creds,
534             FlavorConfig(
535                 name=self.guid + '-flavor-name', ram=256, disk=10, vcpus=1))
536         self.flavor_creator.create()
537
538         env_values = {
539             'public_network': self.ext_net_name,
540             'agent_image': self.image_creator.image_settings.name,
541             'agent_flavor': self.flavor_creator.flavor_settings.name,
542         }
543
544         heat_tmplt_path = pkg_resources.resource_filename(
545             'snaps.openstack.tests.heat', 'agent-group.yaml')
546         heat_resource_path = pkg_resources.resource_filename(
547             'snaps.openstack.tests.heat', 'agent.yaml')
548
549         stack_settings = StackConfig(
550             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
551             template_path=heat_tmplt_path,
552             resource_files=[heat_resource_path],
553             env_values=env_values)
554
555         self.stack_creator = OpenStackHeatStack(
556             self.heat_creds, stack_settings,
557             [self.image_creator.image_settings])
558
559         self.vm_inst_creators = list()
560
561     def tearDown(self):
562         """
563         Cleans the stack and downloaded stack file
564         """
565         if self.stack_creator:
566             try:
567                 self.stack_creator.clean()
568             except:
569                 pass
570
571         if self.image_creator:
572             try:
573                 self.image_creator.clean()
574             except:
575                 pass
576
577         if self.flavor_creator:
578             try:
579                 self.flavor_creator.clean()
580             except:
581                 pass
582
583         for vm_inst_creator in self.vm_inst_creators:
584             try:
585                 keypair_settings = vm_inst_creator.keypair_settings
586                 if keypair_settings and keypair_settings.private_filepath:
587                     expanded_path = os.path.expanduser(
588                         keypair_settings.private_filepath)
589                     os.chmod(expanded_path, 0o755)
590                     os.remove(expanded_path)
591             except:
592                 pass
593
594         super(self.__class__, self).__clean__()
595
596     def test_nested(self):
597         """
598         Tests the creation of an OpenStack stack from Heat template file and
599         the retrieval of two VM instance creators and attempt to connect via
600         SSH to the first one with a floating IP.
601         """
602         created_stack = self.stack_creator.create()
603         self.assertIsNotNone(created_stack)
604
605         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
606             heat_keypair_option='private_key')
607         self.assertIsNotNone(self.vm_inst_creators)
608         self.assertEqual(1, len(self.vm_inst_creators))
609
610         for vm_inst_creator in self.vm_inst_creators:
611             self.assertTrue(
612                 create_instance_tests.validate_ssh_client(vm_inst_creator))
613
614
615 class CreateStackRouterTests(OSIntegrationTestCase):
616     """
617     Tests for the CreateStack class defined in create_stack.py where the
618     target is a Network, Subnet, and Router
619     """
620
621     def setUp(self):
622         """
623         Instantiates the CreateStack object that is responsible for downloading
624         and creating an OS stack file within OpenStack
625         """
626         super(self.__class__, self).__start__()
627
628         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
629
630         self.heat_creds = self.admin_os_creds
631         self.heat_creds.project_name = self.admin_os_creds.project_name
632
633         self.heat_cli = heat_utils.heat_client(self.heat_creds)
634         self.neutron = neutron_utils.neutron_client(self.os_creds)
635         self.stack_creator = None
636
637         self.net_name = self.guid + '-net'
638         self.subnet_name = self.guid + '-subnet'
639         self.router_name = self.guid + '-router'
640
641         self.env_values = {
642             'net_name': self.net_name,
643             'subnet_name': self.subnet_name,
644             'router_name': self.router_name,
645             'external_net_name': self.ext_net_name}
646
647         self.heat_tmplt_path = pkg_resources.resource_filename(
648             'snaps.openstack.tests.heat', 'router_heat_template.yaml')
649
650         stack_settings = StackConfig(
651             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
652             template_path=self.heat_tmplt_path,
653             env_values=self.env_values)
654         self.stack_creator = OpenStackHeatStack(
655             self.heat_creds, stack_settings)
656         self.created_stack = self.stack_creator.create()
657         self.assertIsNotNone(self.created_stack)
658
659     def tearDown(self):
660         """
661         Cleans the stack and downloaded stack file
662         """
663         if self.stack_creator:
664             try:
665                 self.stack_creator.clean()
666             except:
667                 pass
668
669         super(self.__class__, self).__clean__()
670
671     def test_retrieve_router_creator(self):
672         """
673         Tests the creation of an OpenStack stack from Heat template file and
674         the retrieval of an OpenStackRouter creator/state machine instance
675         """
676         router_creators = self.stack_creator.get_router_creators()
677         self.assertEqual(1, len(router_creators))
678
679         creator = router_creators[0]
680         self.assertEqual(self.router_name, creator.router_settings.name)
681
682         router = creator.get_router()
683
684         ext_net = neutron_utils.get_network(
685             self.neutron, network_name=self.ext_net_name)
686         self.assertEqual(ext_net.id, router.external_network_id)
687
688
689 class CreateStackVolumeTests(OSIntegrationTestCase):
690     """
691     Tests to ensure that floating IPs can be accessed via an
692     OpenStackVolume object obtained from the OpenStackHeatStack instance
693     """
694
695     def setUp(self):
696
697         super(self.__class__, self).__start__()
698
699         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
700
701         self.heat_creds = self.admin_os_creds
702         self.heat_creds.project_name = self.admin_os_creds.project_name
703
704         self.heat_cli = heat_utils.heat_client(self.heat_creds)
705         self.stack_creator = None
706
707         self.volume_name = self.guid + '-volume'
708         self.volume_type_name = self.guid + '-volume-type'
709
710         self.env_values = {
711             'volume_name': self.volume_name,
712             'volume_type_name': self.volume_type_name}
713
714         self.heat_tmplt_path = pkg_resources.resource_filename(
715             'snaps.openstack.tests.heat', 'volume_heat_template.yaml')
716
717         stack_settings = StackConfig(
718             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
719             template_path=self.heat_tmplt_path,
720             env_values=self.env_values)
721         self.stack_creator = OpenStackHeatStack(
722             self.heat_creds, stack_settings)
723         self.created_stack = self.stack_creator.create()
724         self.assertIsNotNone(self.created_stack)
725
726     def tearDown(self):
727         """
728         Cleans the stack and downloaded stack file
729         """
730         if self.stack_creator:
731             try:
732                 self.stack_creator.clean()
733             except:
734                 pass
735
736         super(self.__class__, self).__clean__()
737
738     def test_retrieve_volume_creator(self):
739         """
740         Tests the creation of an OpenStack stack from Heat template file and
741         the retrieval of an OpenStackVolume creator/state machine instance
742         """
743         volume_creators = self.stack_creator.get_volume_creators()
744         self.assertEqual(1, len(volume_creators))
745
746         creator = volume_creators[0]
747         self.assertEqual(self.volume_name, creator.volume_settings.name)
748         self.assertEqual(self.volume_name, creator.get_volume().name)
749         self.assertEqual(self.volume_type_name,
750                          creator.volume_settings.type_name)
751         self.assertEqual(self.volume_type_name, creator.get_volume().type)
752         self.assertEqual(1, creator.volume_settings.size)
753         self.assertEqual(1, creator.get_volume().size)
754
755     def test_retrieve_volume_type_creator(self):
756         """
757         Tests the creation of an OpenStack stack from Heat template file and
758         the retrieval of an OpenStackVolume creator/state machine instance
759         """
760         volume_type_creators = self.stack_creator.get_volume_type_creators()
761         self.assertEqual(1, len(volume_type_creators))
762
763         creator = volume_type_creators[0]
764         self.assertIsNotNone(creator)
765
766         volume_type = creator.get_volume_type()
767         self.assertIsNotNone(volume_type)
768
769         self.assertEqual(self.volume_type_name, volume_type.name)
770         self.assertTrue(volume_type.public)
771         self.assertIsNone(volume_type.qos_spec)
772
773         # TODO - Add encryption back and find out why it broke in Pike
774         # encryption = volume_type.encryption
775         # self.assertIsNotNone(encryption)
776         # self.assertIsNone(encryption.cipher)
777         # self.assertEqual('front-end', encryption.control_location)
778         # self.assertIsNone(encryption.key_size)
779         # self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor',
780         #                  encryption.provider)
781         # self.assertEqual(volume_type.id, encryption.volume_type_id)
782
783
784 class CreateStackFlavorTests(OSIntegrationTestCase):
785     """
786     Tests to ensure that floating IPs can be accessed via an
787     OpenStackFlavor object obtained from the OpenStackHeatStack instance
788     """
789
790     def setUp(self):
791
792         super(self.__class__, self).__start__()
793
794         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
795
796         self.heat_creds = self.admin_os_creds
797         self.heat_creds.project_name = self.admin_os_creds.project_name
798
799         self.heat_cli = heat_utils.heat_client(self.heat_creds)
800         self.stack_creator = None
801
802         self.heat_tmplt_path = pkg_resources.resource_filename(
803             'snaps.openstack.tests.heat', 'flavor_heat_template.yaml')
804
805         stack_settings = StackConfig(
806             name=self.guid + '-stack',
807             template_path=self.heat_tmplt_path)
808         self.stack_creator = OpenStackHeatStack(
809             self.heat_creds, stack_settings)
810         self.created_stack = self.stack_creator.create()
811         self.assertIsNotNone(self.created_stack)
812
813     def tearDown(self):
814         """
815         Cleans the stack and downloaded stack file
816         """
817         if self.stack_creator:
818             try:
819                 self.stack_creator.clean()
820             except:
821                 pass
822
823         super(self.__class__, self).__clean__()
824
825     def test_retrieve_flavor_creator(self):
826         """
827         Tests the creation of an OpenStack stack from Heat template file and
828         the retrieval of an OpenStackVolume creator/state machine instance
829         """
830         flavor_creators = self.stack_creator.get_flavor_creators()
831         self.assertEqual(1, len(flavor_creators))
832
833         creator = flavor_creators[0]
834         self.assertTrue(creator.get_flavor().name.startswith(self.guid))
835         self.assertEqual(1024, creator.get_flavor().ram)
836         self.assertEqual(200, creator.get_flavor().disk)
837         self.assertEqual(8, creator.get_flavor().vcpus)
838         self.assertEqual(0, creator.get_flavor().ephemeral)
839         self.assertIsNone(creator.get_flavor().swap)
840         self.assertEqual(1.0, creator.get_flavor().rxtx_factor)
841         self.assertTrue(creator.get_flavor().is_public)
842
843
844 class CreateStackKeypairTests(OSIntegrationTestCase):
845     """
846     Tests to ensure that floating IPs can be accessed via an
847     OpenStackKeypair object obtained from the OpenStackHeatStack instance
848     """
849
850     def setUp(self):
851
852         super(self.__class__, self).__start__()
853
854         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
855
856         self.heat_creds = self.admin_os_creds
857         self.heat_creds.project_name = self.admin_os_creds.project_name
858
859         self.heat_cli = heat_utils.heat_client(self.heat_creds)
860         self.nova = nova_utils.nova_client(self.heat_creds)
861         self.stack_creator = None
862
863         self.keypair_name = self.guid + '-kp'
864
865         self.env_values = {
866             'keypair_name': self.keypair_name}
867
868         self.heat_tmplt_path = pkg_resources.resource_filename(
869             'snaps.openstack.tests.heat', 'keypair_heat_template.yaml')
870
871         stack_settings = StackConfig(
872             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
873             template_path=self.heat_tmplt_path,
874             env_values=self.env_values)
875         self.stack_creator = OpenStackHeatStack(
876             self.heat_creds, stack_settings)
877         self.created_stack = self.stack_creator.create()
878         self.assertIsNotNone(self.created_stack)
879
880         self.keypair_creators = list()
881
882     def tearDown(self):
883         """
884         Cleans the stack and downloaded stack file
885         """
886         if self.stack_creator:
887             try:
888                 self.stack_creator.clean()
889             except:
890                 pass
891         for keypair_creator in self.keypair_creators:
892             try:
893                 keypair_creator.clean()
894             except:
895                 pass
896
897         super(self.__class__, self).__clean__()
898
899     def test_retrieve_keypair_creator(self):
900         """
901         Tests the creation of an OpenStack stack from Heat template file and
902         the retrieval of an OpenStackKeypair creator/state machine instance
903         """
904         self.kp_creators = self.stack_creator.get_keypair_creators(
905             'private_key')
906         self.assertEqual(1, len(self.kp_creators))
907
908         self.keypair_creator = self.kp_creators[0]
909
910         self.assertEqual(self.keypair_name,
911                          self.keypair_creator.get_keypair().name)
912         self.assertIsNotNone(
913             self.keypair_creator.keypair_settings.private_filepath)
914
915         private_file_contents = file_utils.read_file(
916             self.keypair_creator.keypair_settings.private_filepath)
917         self.assertTrue(private_file_contents.startswith(
918             '-----BEGIN RSA PRIVATE KEY-----'))
919
920         keypair = nova_utils.get_keypair_by_id(
921             self.nova, self.keypair_creator.get_keypair().id)
922         self.assertIsNotNone(keypair)
923         self.assertEqual(self.keypair_creator.get_keypair(), keypair)
924
925
926 class CreateStackSecurityGroupTests(OSIntegrationTestCase):
927     """
928     Tests for the OpenStackHeatStack class to ensure it returns an
929     OpenStackSecurityGroup object
930     """
931
932     def setUp(self):
933         """
934         Instantiates the CreateStack object that is responsible for downloading
935         and creating an OS stack file within OpenStack
936         """
937         super(self.__class__, self).__start__()
938
939         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
940
941         self.heat_creds = self.admin_os_creds
942         self.heat_creds.project_name = self.admin_os_creds.project_name
943
944         self.heat_cli = heat_utils.heat_client(self.heat_creds)
945         self.nova = nova_utils.nova_client(self.heat_creds)
946         self.stack_creator = None
947
948         self.security_group_name = self.guid + '-sec-grp'
949
950         self.env_values = {
951             'security_group_name': self.security_group_name}
952
953         self.heat_tmplt_path = pkg_resources.resource_filename(
954             'snaps.openstack.tests.heat', 'security_group_heat_template.yaml')
955
956         stack_settings = StackConfig(
957             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
958             template_path=self.heat_tmplt_path,
959             env_values=self.env_values)
960         self.stack_creator = OpenStackHeatStack(
961             self.heat_creds, stack_settings)
962         self.created_stack = self.stack_creator.create()
963         self.assertIsNotNone(self.created_stack)
964
965     def tearDown(self):
966         """
967         Cleans the stack and downloaded stack file
968         """
969         if self.stack_creator:
970             try:
971                 self.stack_creator.clean()
972             except:
973                 pass
974
975         super(self.__class__, self).__clean__()
976
977     def test_retrieve_security_group_creator(self):
978         """
979         Tests the creation of an OpenStack stack from Heat template file and
980         the retrieval of an OpenStackSecurityGroup creator/state machine
981         instance
982         """
983         sec_grp_creators = self.stack_creator.get_security_group_creators()
984         self.assertEqual(1, len(sec_grp_creators))
985
986         creator = sec_grp_creators[0]
987         sec_grp = creator.get_security_group()
988
989         self.assertEqual(self.security_group_name, sec_grp.name)
990         self.assertEqual('Test description', sec_grp.description)
991         self.assertEqual(2, len(sec_grp.rules))
992
993         has_ssh_rule = False
994         has_icmp_rule = False
995
996         for rule in sec_grp.rules:
997             if (rule.security_group_id == sec_grp.id
998                     and rule.direction == 'egress'
999                     and rule.ethertype == 'IPv4'
1000                     and rule.port_range_min == 22
1001                     and rule.port_range_max == 22
1002                     and rule.protocol == 'tcp'
1003                     and rule.remote_group_id is None
1004                     and rule.remote_ip_prefix == '0.0.0.0/0'):
1005                 has_ssh_rule = True
1006             if (rule.security_group_id == sec_grp.id
1007                     and rule.direction == 'ingress'
1008                     and rule.ethertype == 'IPv4'
1009                     and rule.port_range_min is None
1010                     and rule.port_range_max is None
1011                     and rule.protocol == 'icmp'
1012                     and rule.remote_group_id is None
1013                     and rule.remote_ip_prefix == '0.0.0.0/0'):
1014                 has_icmp_rule = True
1015
1016         self.assertTrue(has_ssh_rule)
1017         self.assertTrue(has_icmp_rule)
1018
1019
1020 class CreateStackNegativeTests(OSIntegrationTestCase):
1021     """
1022     Negative test cases for the OpenStackHeatStack class with poor
1023     configuration
1024     """
1025
1026     def setUp(self):
1027
1028         super(self.__class__, self).__start__()
1029
1030         self.heat_creds = self.admin_os_creds
1031         self.heat_creds.project_name = self.admin_os_creds.project_name
1032
1033         self.stack_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
1034         self.stack_creator = None
1035         self.heat_tmplt_path = pkg_resources.resource_filename(
1036             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
1037
1038     def tearDown(self):
1039         if self.stack_creator:
1040             self.stack_creator.clean()
1041         super(self.__class__, self).__clean__()
1042
1043     def test_missing_dependencies(self):
1044         """
1045         Expect an StackCreationError when the stack file does not exist
1046         """
1047         stack_settings = StackConfig(name=self.stack_name,
1048                                      template_path=self.heat_tmplt_path)
1049         self.stack_creator = OpenStackHeatStack(
1050             self.heat_creds, stack_settings)
1051         with self.assertRaises(HTTPBadRequest):
1052             self.stack_creator.create()
1053
1054     def test_bad_stack_file(self):
1055         """
1056         Expect an StackCreationError when the stack file does not exist
1057         """
1058         stack_settings = StackConfig(
1059             name=self.stack_name, template_path='foo')
1060         self.stack_creator = OpenStackHeatStack(
1061             self.heat_creds, stack_settings)
1062         with self.assertRaises(IOError):
1063             self.stack_creator.create()
1064
1065
1066 class CreateStackFailureTests(OSIntegrationTestCase):
1067     """
1068     Tests for the OpenStackHeatStack class defined in create_stack.py for
1069     when failures occur. Failures are being triggered by allocating 1 million
1070     CPUs.
1071     """
1072
1073     def setUp(self):
1074
1075         super(self.__class__, self).__start__()
1076
1077         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
1078
1079         self.heat_creds = self.admin_os_creds
1080         self.heat_creds.project_name = self.admin_os_creds.project_name
1081
1082         self.heat_cli = heat_utils.heat_client(self.heat_creds)
1083         self.stack_creator = None
1084
1085         self.tmp_file = file_utils.save_string_to_file(
1086             ' ', str(uuid.uuid4()) + '-bad-image')
1087         self.image_creator = OpenStackImage(
1088             self.heat_creds, ImageConfig(
1089                 name=self.guid + 'image', image_file=self.tmp_file.name,
1090                 image_user='foo', img_format='qcow2'))
1091         self.image_creator.create()
1092
1093         # Create Flavor
1094         self.flavor_creator = OpenStackFlavor(
1095             self.admin_os_creds,
1096             FlavorConfig(
1097                 name=self.guid + '-flavor-name', ram=256, disk=10,
1098                 vcpus=1000000))
1099         self.flavor_creator.create()
1100
1101         self.network_name = self.guid + '-net'
1102         self.subnet_name = self.guid + '-subnet'
1103         self.vm_inst_name = self.guid + '-inst'
1104
1105         self.env_values = {
1106             'image_name': self.image_creator.image_settings.name,
1107             'flavor_name': self.flavor_creator.flavor_settings.name,
1108             'net_name': self.network_name,
1109             'subnet_name': self.subnet_name,
1110             'inst_name': self.vm_inst_name}
1111
1112         self.heat_tmplt_path = pkg_resources.resource_filename(
1113             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
1114
1115     def tearDown(self):
1116         """
1117         Cleans the stack and downloaded stack file
1118         """
1119         if self.stack_creator:
1120             try:
1121                 self.stack_creator.clean()
1122             except:
1123                 pass
1124
1125         if self.image_creator:
1126             try:
1127                 self.image_creator.clean()
1128             except:
1129                 pass
1130
1131         if self.flavor_creator:
1132             try:
1133                 self.flavor_creator.clean()
1134             except:
1135                 pass
1136
1137         if self.tmp_file:
1138             try:
1139                 os.remove(self.tmp_file.name)
1140             except:
1141                 pass
1142
1143         super(self.__class__, self).__clean__()
1144
1145     def test_stack_failure(self):
1146         """
1147         Tests the creation of an OpenStack stack from Heat template file that
1148         should always fail due to too many CPU cores
1149         """
1150         # Create Stack
1151         # Set the default stack settings, then set any custom parameters sent
1152         # from the app
1153         stack_settings = StackConfig(
1154             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
1155             template_path=self.heat_tmplt_path,
1156             env_values=self.env_values)
1157         self.stack_creator = OpenStackHeatStack(
1158             self.heat_creds, stack_settings)
1159
1160         with self.assertRaises(StackError):
1161             try:
1162                 self.stack_creator.create()
1163             except StackError:
1164                 resources = heat_utils.get_resources(
1165                     self.heat_cli, self.stack_creator.get_stack().id)
1166
1167                 found = False
1168                 for resource in resources:
1169                     if (resource.status ==
1170                             snaps.config.stack.STATUS_CREATE_COMPLETE):
1171                         found = True
1172                 self.assertTrue(found)
1173                 raise