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