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