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