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