Changed pattern on how objects lookup themselves by name and project.
[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
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         # Need to use 'admin' creds as heat creates objects under it's own
355         # project/tenant
356         neutron = neutron_utils.neutron_client(self.admin_os_creds)
357         keystone = keystone_utils.keystone_client(self.admin_os_creds)
358         net_by_name = neutron_utils.get_network(
359             neutron, keystone, network_name=net_creators[0].get_network().name)
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         keystone = keystone_utils.keystone_client(self.admin_os_creds)
397         vm_inst_by_name = nova_utils.get_server(
398             nova, neutron, keystone,
399             server_name=vm_inst_creators[0].get_vm_inst().name)
400
401         self.assertEqual(vm_inst_creators[0].get_vm_inst(), vm_inst_by_name)
402         self.assertIsNotNone(nova_utils.get_server_object_by_id(
403             nova, neutron, keystone, vm_inst_creators[0].get_vm_inst().id))
404
405
406 class CreateStackFloatingIpTests(OSIntegrationTestCase):
407     """
408     Tests to ensure that floating IPs can be accessed via an
409     OpenStackVmInstance object obtained from the OpenStackHeatStack instance
410     """
411
412     def setUp(self):
413
414         super(self.__class__, self).__start__()
415
416         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
417
418         self.heat_creds = self.admin_os_creds
419         self.heat_creds.project_name = self.admin_os_creds.project_name
420
421         self.heat_cli = heat_utils.heat_client(self.heat_creds)
422         self.stack_creator = None
423
424         self.image_creator = OpenStackImage(
425             self.heat_creds, openstack_tests.cirros_image_settings(
426                 name=self.guid + '-image',
427                 image_metadata=self.image_metadata))
428         self.image_creator.create()
429
430         self.network_name = self.guid + '-net'
431         self.subnet_name = self.guid + '-subnet'
432         self.flavor1_name = self.guid + '-flavor1'
433         self.flavor2_name = self.guid + '-flavor2'
434         self.sec_grp_name = self.guid + '-sec_grp'
435         self.vm_inst1_name = self.guid + '-inst1'
436         self.vm_inst2_name = self.guid + '-inst2'
437         self.keypair_name = self.guid + '-kp'
438
439         self.env_values = {
440             'image1_name': self.image_creator.image_settings.name,
441             'image2_name': self.image_creator.image_settings.name,
442             'flavor1_name': self.flavor1_name,
443             'flavor2_name': self.flavor2_name,
444             'net_name': self.network_name,
445             'subnet_name': self.subnet_name,
446             'inst1_name': self.vm_inst1_name,
447             'inst2_name': self.vm_inst2_name,
448             'keypair_name': self.keypair_name,
449             'external_net_name': self.ext_net_name,
450             'security_group_name': self.sec_grp_name}
451
452         self.heat_tmplt_path = pkg_resources.resource_filename(
453             'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
454
455         self.vm_inst_creators = list()
456
457     def tearDown(self):
458         """
459         Cleans the stack and downloaded stack file
460         """
461         if self.stack_creator:
462             try:
463                 self.stack_creator.clean()
464             except:
465                 pass
466
467         if self.image_creator:
468             try:
469                 self.image_creator.clean()
470             except:
471                 pass
472
473         for vm_inst_creator in self.vm_inst_creators:
474             try:
475                 keypair_settings = vm_inst_creator.keypair_settings
476                 if keypair_settings and keypair_settings.private_filepath:
477                     expanded_path = os.path.expanduser(
478                         keypair_settings.private_filepath)
479                     os.chmod(expanded_path, 0o755)
480                     os.remove(expanded_path)
481             except:
482                 pass
483
484         super(self.__class__, self).__clean__()
485
486     def test_connect_via_ssh_heat_vm(self):
487         """
488         Tests the creation of an OpenStack stack from Heat template file and
489         the retrieval of two VM instance creators and attempt to connect via
490         SSH to the first one with a floating IP.
491         """
492         stack_settings = StackConfig(
493             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
494             template_path=self.heat_tmplt_path,
495             env_values=self.env_values)
496         self.stack_creator = OpenStackHeatStack(
497             self.heat_creds, stack_settings,
498             [self.image_creator.image_settings])
499         created_stack = self.stack_creator.create()
500         self.assertIsNotNone(created_stack)
501
502         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
503             heat_keypair_option='private_key')
504         self.assertIsNotNone(self.vm_inst_creators)
505         self.assertEqual(2, len(self.vm_inst_creators))
506
507         for vm_inst_creator in self.vm_inst_creators:
508             if vm_inst_creator.get_vm_inst().name == self.vm_inst1_name:
509                 self.assertTrue(
510                     create_instance_tests.validate_ssh_client(vm_inst_creator))
511             else:
512                 vm_settings = vm_inst_creator.instance_settings
513                 self.assertEqual(0, len(vm_settings.floating_ip_settings))
514
515
516 class CreateStackNestedResourceTests(OSIntegrationTestCase):
517     """
518     Tests to ensure that nested heat templates work
519     """
520
521     def setUp(self):
522
523         super(self.__class__, self).__start__()
524
525         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
526
527         self.heat_creds = self.admin_os_creds
528         self.heat_creds.project_name = self.admin_os_creds.project_name
529
530         self.heat_cli = heat_utils.heat_client(self.heat_creds)
531         self.stack_creator = None
532
533         self.image_creator = OpenStackImage(
534             self.heat_creds, openstack_tests.cirros_image_settings(
535                 name=self.guid + '-image',
536                 image_metadata=self.image_metadata))
537         self.image_creator.create()
538
539         self.flavor_creator = OpenStackFlavor(
540             self.admin_os_creds,
541             FlavorConfig(
542                 name=self.guid + '-flavor-name', ram=256, disk=10, vcpus=1))
543         self.flavor_creator.create()
544
545         env_values = {
546             'network_name': self.guid + '-network',
547             'public_network': self.ext_net_name,
548             'agent_image': self.image_creator.image_settings.name,
549             'agent_flavor': self.flavor_creator.flavor_settings.name,
550             'key_name': self.guid + '-key',
551         }
552
553         heat_tmplt_path = pkg_resources.resource_filename(
554             'snaps.openstack.tests.heat', 'agent-group.yaml')
555         heat_resource_path = pkg_resources.resource_filename(
556             'snaps.openstack.tests.heat', 'agent.yaml')
557
558         stack_settings = StackConfig(
559             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
560             template_path=heat_tmplt_path,
561             resource_files=[heat_resource_path],
562             env_values=env_values)
563
564         self.stack_creator = OpenStackHeatStack(
565             self.heat_creds, stack_settings,
566             [self.image_creator.image_settings])
567
568         self.vm_inst_creators = list()
569
570     def tearDown(self):
571         """
572         Cleans the stack and downloaded stack file
573         """
574         if self.stack_creator:
575             try:
576                 self.stack_creator.clean()
577             except:
578                 pass
579
580         if self.image_creator:
581             try:
582                 self.image_creator.clean()
583             except:
584                 pass
585
586         if self.flavor_creator:
587             try:
588                 self.flavor_creator.clean()
589             except:
590                 pass
591
592         for vm_inst_creator in self.vm_inst_creators:
593             try:
594                 keypair_settings = vm_inst_creator.keypair_settings
595                 if keypair_settings and keypair_settings.private_filepath:
596                     expanded_path = os.path.expanduser(
597                         keypair_settings.private_filepath)
598                     os.chmod(expanded_path, 0o755)
599                     os.remove(expanded_path)
600             except:
601                 pass
602
603         super(self.__class__, self).__clean__()
604
605     def test_nested(self):
606         """
607         Tests the creation of an OpenStack stack from Heat template file and
608         the retrieval of two VM instance creators and attempt to connect via
609         SSH to the first one with a floating IP.
610         """
611         created_stack = self.stack_creator.create()
612         self.assertIsNotNone(created_stack)
613
614         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
615             heat_keypair_option='private_key')
616         self.assertIsNotNone(self.vm_inst_creators)
617         self.assertEqual(1, len(self.vm_inst_creators))
618
619         for vm_inst_creator in self.vm_inst_creators:
620             self.assertTrue(
621                 create_instance_tests.validate_ssh_client(vm_inst_creator))
622
623
624 class CreateStackRouterTests(OSIntegrationTestCase):
625     """
626     Tests for the CreateStack class defined in create_stack.py where the
627     target is a Network, Subnet, and Router
628     """
629
630     def setUp(self):
631         """
632         Instantiates the CreateStack object that is responsible for downloading
633         and creating an OS stack file within OpenStack
634         """
635         super(self.__class__, self).__start__()
636
637         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
638
639         self.heat_creds = self.admin_os_creds
640         self.heat_creds.project_name = self.admin_os_creds.project_name
641
642         self.heat_cli = heat_utils.heat_client(self.heat_creds)
643         self.neutron = neutron_utils.neutron_client(self.os_creds)
644         self.stack_creator = None
645
646         self.net_name = self.guid + '-net'
647         self.subnet_name = self.guid + '-subnet'
648         self.router_name = self.guid + '-router'
649
650         self.env_values = {
651             'net_name': self.net_name,
652             'subnet_name': self.subnet_name,
653             'router_name': self.router_name,
654             'external_net_name': self.ext_net_name}
655
656         self.heat_tmplt_path = pkg_resources.resource_filename(
657             'snaps.openstack.tests.heat', 'router_heat_template.yaml')
658
659         stack_settings = StackConfig(
660             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
661             template_path=self.heat_tmplt_path,
662             env_values=self.env_values)
663         self.stack_creator = OpenStackHeatStack(
664             self.heat_creds, stack_settings)
665         self.created_stack = self.stack_creator.create()
666         self.assertIsNotNone(self.created_stack)
667
668     def tearDown(self):
669         """
670         Cleans the stack and downloaded stack file
671         """
672         if self.stack_creator:
673             try:
674                 self.stack_creator.clean()
675             except:
676                 pass
677
678         super(self.__class__, self).__clean__()
679
680     def test_retrieve_router_creator(self):
681         """
682         Tests the creation of an OpenStack stack from Heat template file and
683         the retrieval of an OpenStackRouter creator/state machine instance
684         """
685         router_creators = self.stack_creator.get_router_creators()
686         self.assertEqual(1, len(router_creators))
687
688         creator = router_creators[0]
689         self.assertEqual(self.router_name, creator.router_settings.name)
690
691         router = creator.get_router()
692
693         ext_net = neutron_utils.get_network(
694             self.neutron, self.keystone, network_name=self.ext_net_name)
695         self.assertEqual(ext_net.id, router.external_network_id)
696
697
698 class CreateStackVolumeTests(OSIntegrationTestCase):
699     """
700     Tests to ensure that floating IPs can be accessed via an
701     OpenStackVolume object obtained from the OpenStackHeatStack instance
702     """
703
704     def setUp(self):
705
706         super(self.__class__, self).__start__()
707
708         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
709
710         self.heat_creds = self.admin_os_creds
711         self.heat_creds.project_name = self.admin_os_creds.project_name
712
713         self.heat_cli = heat_utils.heat_client(self.heat_creds)
714         self.stack_creator = None
715
716         self.volume_name = self.guid + '-volume'
717         self.volume_type_name = self.guid + '-volume-type'
718
719         self.env_values = {
720             'volume_name': self.volume_name,
721             'volume_type_name': self.volume_type_name}
722
723         self.heat_tmplt_path = pkg_resources.resource_filename(
724             'snaps.openstack.tests.heat', 'volume_heat_template.yaml')
725
726         stack_settings = StackConfig(
727             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
728             template_path=self.heat_tmplt_path,
729             env_values=self.env_values)
730         self.stack_creator = OpenStackHeatStack(
731             self.heat_creds, stack_settings)
732         self.created_stack = self.stack_creator.create()
733         self.assertIsNotNone(self.created_stack)
734
735     def tearDown(self):
736         """
737         Cleans the stack and downloaded stack file
738         """
739         if self.stack_creator:
740             try:
741                 self.stack_creator.clean()
742             except:
743                 pass
744
745         super(self.__class__, self).__clean__()
746
747     def test_retrieve_volume_creator(self):
748         """
749         Tests the creation of an OpenStack stack from Heat template file and
750         the retrieval of an OpenStackVolume creator/state machine instance
751         """
752         volume_creators = self.stack_creator.get_volume_creators()
753         self.assertEqual(1, len(volume_creators))
754
755         creator = volume_creators[0]
756         self.assertEqual(self.volume_name, creator.volume_settings.name)
757         self.assertEqual(self.volume_name, creator.get_volume().name)
758         self.assertEqual(self.volume_type_name,
759                          creator.volume_settings.type_name)
760         self.assertEqual(self.volume_type_name, creator.get_volume().type)
761         self.assertEqual(1, creator.volume_settings.size)
762         self.assertEqual(1, creator.get_volume().size)
763
764     def test_retrieve_volume_type_creator(self):
765         """
766         Tests the creation of an OpenStack stack from Heat template file and
767         the retrieval of an OpenStackVolume creator/state machine instance
768         """
769         volume_type_creators = self.stack_creator.get_volume_type_creators()
770         self.assertEqual(1, len(volume_type_creators))
771
772         creator = volume_type_creators[0]
773         self.assertIsNotNone(creator)
774
775         volume_type = creator.get_volume_type()
776         self.assertIsNotNone(volume_type)
777
778         self.assertEqual(self.volume_type_name, volume_type.name)
779         self.assertTrue(volume_type.public)
780         self.assertIsNone(volume_type.qos_spec)
781
782         # TODO - Add encryption back and find out why it broke in Pike
783         # encryption = volume_type.encryption
784         # self.assertIsNotNone(encryption)
785         # self.assertIsNone(encryption.cipher)
786         # self.assertEqual('front-end', encryption.control_location)
787         # self.assertIsNone(encryption.key_size)
788         # self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor',
789         #                  encryption.provider)
790         # self.assertEqual(volume_type.id, encryption.volume_type_id)
791
792
793 class CreateStackFlavorTests(OSIntegrationTestCase):
794     """
795     Tests to ensure that floating IPs can be accessed via an
796     OpenStackFlavor object obtained from the OpenStackHeatStack instance
797     """
798
799     def setUp(self):
800
801         super(self.__class__, self).__start__()
802
803         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
804
805         self.heat_creds = self.admin_os_creds
806         self.heat_creds.project_name = self.admin_os_creds.project_name
807
808         self.heat_cli = heat_utils.heat_client(self.heat_creds)
809         self.stack_creator = None
810
811         self.heat_tmplt_path = pkg_resources.resource_filename(
812             'snaps.openstack.tests.heat', 'flavor_heat_template.yaml')
813
814         stack_settings = StackConfig(
815             name=self.guid + '-stack',
816             template_path=self.heat_tmplt_path)
817         self.stack_creator = OpenStackHeatStack(
818             self.heat_creds, stack_settings)
819         self.created_stack = self.stack_creator.create()
820         self.assertIsNotNone(self.created_stack)
821
822     def tearDown(self):
823         """
824         Cleans the stack and downloaded stack file
825         """
826         if self.stack_creator:
827             try:
828                 self.stack_creator.clean()
829             except:
830                 pass
831
832         super(self.__class__, self).__clean__()
833
834     def test_retrieve_flavor_creator(self):
835         """
836         Tests the creation of an OpenStack stack from Heat template file and
837         the retrieval of an OpenStackVolume creator/state machine instance
838         """
839         flavor_creators = self.stack_creator.get_flavor_creators()
840         self.assertEqual(1, len(flavor_creators))
841
842         creator = flavor_creators[0]
843         self.assertTrue(creator.get_flavor().name.startswith(self.guid))
844         self.assertEqual(1024, creator.get_flavor().ram)
845         self.assertEqual(200, creator.get_flavor().disk)
846         self.assertEqual(8, creator.get_flavor().vcpus)
847         self.assertEqual(0, creator.get_flavor().ephemeral)
848         self.assertIsNone(creator.get_flavor().swap)
849         self.assertEqual(1.0, creator.get_flavor().rxtx_factor)
850         self.assertTrue(creator.get_flavor().is_public)
851
852
853 class CreateStackKeypairTests(OSIntegrationTestCase):
854     """
855     Tests to ensure that floating IPs can be accessed via an
856     OpenStackKeypair object obtained from the OpenStackHeatStack instance
857     """
858
859     def setUp(self):
860
861         super(self.__class__, self).__start__()
862
863         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
864
865         self.heat_creds = self.admin_os_creds
866         self.heat_creds.project_name = self.admin_os_creds.project_name
867
868         self.heat_cli = heat_utils.heat_client(self.heat_creds)
869         self.nova = nova_utils.nova_client(self.heat_creds)
870         self.stack_creator = None
871
872         self.keypair_name = self.guid + '-kp'
873
874         self.env_values = {
875             'keypair_name': self.keypair_name}
876
877         self.heat_tmplt_path = pkg_resources.resource_filename(
878             'snaps.openstack.tests.heat', 'keypair_heat_template.yaml')
879
880         stack_settings = StackConfig(
881             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
882             template_path=self.heat_tmplt_path,
883             env_values=self.env_values)
884         self.stack_creator = OpenStackHeatStack(
885             self.heat_creds, stack_settings)
886         self.created_stack = self.stack_creator.create()
887         self.assertIsNotNone(self.created_stack)
888
889         self.keypair_creators = list()
890
891     def tearDown(self):
892         """
893         Cleans the stack and downloaded stack file
894         """
895         if self.stack_creator:
896             try:
897                 self.stack_creator.clean()
898             except:
899                 pass
900         for keypair_creator in self.keypair_creators:
901             try:
902                 keypair_creator.clean()
903             except:
904                 pass
905
906         super(self.__class__, self).__clean__()
907
908     def test_retrieve_keypair_creator(self):
909         """
910         Tests the creation of an OpenStack stack from Heat template file and
911         the retrieval of an OpenStackKeypair creator/state machine instance
912         """
913         self.kp_creators = self.stack_creator.get_keypair_creators(
914             'private_key')
915         self.assertEqual(1, len(self.kp_creators))
916
917         self.keypair_creator = self.kp_creators[0]
918
919         self.assertEqual(self.keypair_name,
920                          self.keypair_creator.get_keypair().name)
921         self.assertIsNotNone(
922             self.keypair_creator.keypair_settings.private_filepath)
923
924         private_file_contents = file_utils.read_file(
925             self.keypair_creator.keypair_settings.private_filepath)
926         self.assertTrue(private_file_contents.startswith(
927             '-----BEGIN RSA PRIVATE KEY-----'))
928
929         keypair = nova_utils.get_keypair_by_id(
930             self.nova, self.keypair_creator.get_keypair().id)
931         self.assertIsNotNone(keypair)
932         self.assertEqual(self.keypair_creator.get_keypair(), keypair)
933
934
935 class CreateStackSecurityGroupTests(OSIntegrationTestCase):
936     """
937     Tests for the OpenStackHeatStack class to ensure it returns an
938     OpenStackSecurityGroup object
939     """
940
941     def setUp(self):
942         """
943         Instantiates the CreateStack object that is responsible for downloading
944         and creating an OS stack file within OpenStack
945         """
946         super(self.__class__, self).__start__()
947
948         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
949
950         self.heat_creds = self.admin_os_creds
951         self.heat_creds.project_name = self.admin_os_creds.project_name
952
953         self.heat_cli = heat_utils.heat_client(self.heat_creds)
954         self.nova = nova_utils.nova_client(self.heat_creds)
955         self.stack_creator = None
956
957         self.security_group_name = self.guid + '-sec-grp'
958
959         self.env_values = {
960             'security_group_name': self.security_group_name}
961
962         self.heat_tmplt_path = pkg_resources.resource_filename(
963             'snaps.openstack.tests.heat', 'security_group_heat_template.yaml')
964
965         stack_settings = StackConfig(
966             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
967             template_path=self.heat_tmplt_path,
968             env_values=self.env_values)
969         self.stack_creator = OpenStackHeatStack(
970             self.heat_creds, stack_settings)
971         self.created_stack = self.stack_creator.create()
972         self.assertIsNotNone(self.created_stack)
973
974     def tearDown(self):
975         """
976         Cleans the stack and downloaded stack file
977         """
978         if self.stack_creator:
979             try:
980                 self.stack_creator.clean()
981             except:
982                 pass
983
984         super(self.__class__, self).__clean__()
985
986     def test_retrieve_security_group_creator(self):
987         """
988         Tests the creation of an OpenStack stack from Heat template file and
989         the retrieval of an OpenStackSecurityGroup creator/state machine
990         instance
991         """
992         sec_grp_creators = self.stack_creator.get_security_group_creators()
993         self.assertEqual(1, len(sec_grp_creators))
994
995         creator = sec_grp_creators[0]
996         sec_grp = creator.get_security_group()
997
998         self.assertEqual(self.security_group_name, sec_grp.name)
999         self.assertEqual('Test description', sec_grp.description)
1000         self.assertEqual(2, len(sec_grp.rules))
1001
1002         has_ssh_rule = False
1003         has_icmp_rule = False
1004
1005         for rule in sec_grp.rules:
1006             if (rule.security_group_id == sec_grp.id
1007                     and rule.direction == 'egress'
1008                     and rule.ethertype == 'IPv4'
1009                     and rule.port_range_min == 22
1010                     and rule.port_range_max == 22
1011                     and rule.protocol == 'tcp'
1012                     and rule.remote_group_id is None
1013                     and rule.remote_ip_prefix == '0.0.0.0/0'):
1014                 has_ssh_rule = True
1015             if (rule.security_group_id == sec_grp.id
1016                     and rule.direction == 'ingress'
1017                     and rule.ethertype == 'IPv4'
1018                     and rule.port_range_min is None
1019                     and rule.port_range_max is None
1020                     and rule.protocol == 'icmp'
1021                     and rule.remote_group_id is None
1022                     and rule.remote_ip_prefix == '0.0.0.0/0'):
1023                 has_icmp_rule = True
1024
1025         self.assertTrue(has_ssh_rule)
1026         self.assertTrue(has_icmp_rule)
1027
1028
1029 class CreateStackNegativeTests(OSIntegrationTestCase):
1030     """
1031     Negative test cases for the OpenStackHeatStack class with poor
1032     configuration
1033     """
1034
1035     def setUp(self):
1036
1037         super(self.__class__, self).__start__()
1038
1039         self.heat_creds = self.admin_os_creds
1040         self.heat_creds.project_name = self.admin_os_creds.project_name
1041
1042         self.stack_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
1043         self.stack_creator = None
1044         self.heat_tmplt_path = pkg_resources.resource_filename(
1045             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
1046
1047     def tearDown(self):
1048         if self.stack_creator:
1049             self.stack_creator.clean()
1050         super(self.__class__, self).__clean__()
1051
1052     def test_missing_dependencies(self):
1053         """
1054         Expect an StackCreationError when the stack file does not exist
1055         """
1056         stack_settings = StackConfig(name=self.stack_name,
1057                                      template_path=self.heat_tmplt_path)
1058         self.stack_creator = OpenStackHeatStack(
1059             self.heat_creds, stack_settings)
1060         with self.assertRaises(HTTPBadRequest):
1061             self.stack_creator.create()
1062
1063     def test_bad_stack_file(self):
1064         """
1065         Expect an StackCreationError when the stack file does not exist
1066         """
1067         stack_settings = StackConfig(
1068             name=self.stack_name, template_path='foo')
1069         self.stack_creator = OpenStackHeatStack(
1070             self.heat_creds, stack_settings)
1071         with self.assertRaises(IOError):
1072             self.stack_creator.create()
1073
1074
1075 class CreateStackFailureTests(OSIntegrationTestCase):
1076     """
1077     Tests for the OpenStackHeatStack class defined in create_stack.py for
1078     when failures occur. Failures are being triggered by allocating 1 million
1079     CPUs.
1080     """
1081
1082     def setUp(self):
1083
1084         super(self.__class__, self).__start__()
1085
1086         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
1087
1088         self.heat_creds = self.admin_os_creds
1089         self.heat_creds.project_name = self.admin_os_creds.project_name
1090
1091         self.heat_cli = heat_utils.heat_client(self.heat_creds)
1092         self.stack_creator = None
1093
1094         self.tmp_file = file_utils.save_string_to_file(
1095             ' ', str(uuid.uuid4()) + '-bad-image')
1096         self.image_creator = OpenStackImage(
1097             self.heat_creds, ImageConfig(
1098                 name=self.guid + 'image', image_file=self.tmp_file.name,
1099                 image_user='foo', img_format='qcow2'))
1100         self.image_creator.create()
1101
1102         # Create Flavor
1103         self.flavor_creator = OpenStackFlavor(
1104             self.admin_os_creds,
1105             FlavorConfig(
1106                 name=self.guid + '-flavor-name', ram=256, disk=10,
1107                 vcpus=1000000))
1108         self.flavor_creator.create()
1109
1110         self.network_name = self.guid + '-net'
1111         self.subnet_name = self.guid + '-subnet'
1112         self.vm_inst_name = self.guid + '-inst'
1113
1114         self.env_values = {
1115             'image_name': self.image_creator.image_settings.name,
1116             'flavor_name': self.flavor_creator.flavor_settings.name,
1117             'net_name': self.network_name,
1118             'subnet_name': self.subnet_name,
1119             'inst_name': self.vm_inst_name}
1120
1121         self.heat_tmplt_path = pkg_resources.resource_filename(
1122             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
1123
1124     def tearDown(self):
1125         """
1126         Cleans the stack and downloaded stack file
1127         """
1128         if self.stack_creator:
1129             try:
1130                 self.stack_creator.clean()
1131             except:
1132                 pass
1133
1134         if self.image_creator:
1135             try:
1136                 self.image_creator.clean()
1137             except:
1138                 pass
1139
1140         if self.flavor_creator:
1141             try:
1142                 self.flavor_creator.clean()
1143             except:
1144                 pass
1145
1146         if self.tmp_file:
1147             try:
1148                 os.remove(self.tmp_file.name)
1149             except:
1150                 pass
1151
1152         super(self.__class__, self).__clean__()
1153
1154     def test_stack_failure(self):
1155         """
1156         Tests the creation of an OpenStack stack from Heat template file that
1157         should always fail due to too many CPU cores
1158         """
1159         # Create Stack
1160         # Set the default stack settings, then set any custom parameters sent
1161         # from the app
1162         stack_settings = StackConfig(
1163             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
1164             template_path=self.heat_tmplt_path,
1165             env_values=self.env_values)
1166         self.stack_creator = OpenStackHeatStack(
1167             self.heat_creds, stack_settings)
1168
1169         with self.assertRaises(StackError):
1170             try:
1171                 self.stack_creator.create()
1172             except StackError:
1173                 resources = heat_utils.get_resources(
1174                     self.heat_cli, self.stack_creator.get_stack().id)
1175
1176                 found = False
1177                 for resource in resources:
1178                     if (resource.status ==
1179                             snaps.config.stack.STATUS_CREATE_COMPLETE):
1180                         found = True
1181                 self.assertTrue(found)
1182                 raise