Added MTU network config and updated flavor_metadata test support
[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="{}-{}".format(self.guid, 'image'),
573                 image_metadata=self.image_metadata))
574         self.image_creator.create()
575
576         flavor_config = openstack_tests.get_flavor_config(
577             name="{}-{}".format(self.guid, 'flavor-name'), ram=256, disk=10,
578             vcpus=1, metadata=self.flavor_metadata)
579         self.flavor_creator = OpenStackFlavor(
580             self.admin_os_creds, flavor_config)
581         self.flavor_creator.create()
582
583         env_values = {
584             'network_name': self.guid + '-network',
585             'public_network': self.ext_net_name,
586             'agent_image': self.image_creator.image_settings.name,
587             'agent_flavor': self.flavor_creator.flavor_settings.name,
588             'key_name': self.guid + '-key',
589         }
590
591         heat_tmplt_path = pkg_resources.resource_filename(
592             'snaps.openstack.tests.heat', 'agent-group.yaml')
593         heat_resource_path = pkg_resources.resource_filename(
594             'snaps.openstack.tests.heat', 'agent.yaml')
595
596         stack_settings = StackConfig(
597             name="{}-{}".format(
598                 self.__class__.__name__, str(self.guid) + '-stack'),
599             template_path=heat_tmplt_path,
600             resource_files=[heat_resource_path],
601             env_values=env_values)
602
603         self.stack_creator = OpenStackHeatStack(
604             self.os_creds, stack_settings,
605             [self.image_creator.image_settings])
606
607         self.vm_inst_creators = list()
608
609     def tearDown(self):
610         """
611         Cleans the stack and downloaded stack file
612         """
613         if self.stack_creator:
614             try:
615                 self.stack_creator.clean()
616             except:
617                 pass
618
619         if self.image_creator:
620             try:
621                 self.image_creator.clean()
622             except:
623                 pass
624
625         if self.flavor_creator:
626             try:
627                 self.flavor_creator.clean()
628             except:
629                 pass
630
631         for vm_inst_creator in self.vm_inst_creators:
632             try:
633                 keypair_settings = vm_inst_creator.keypair_settings
634                 if keypair_settings and keypair_settings.private_filepath:
635                     expanded_path = os.path.expanduser(
636                         keypair_settings.private_filepath)
637                     os.chmod(expanded_path, 0o755)
638                     os.remove(expanded_path)
639             except:
640                 pass
641
642         super(self.__class__, self).__clean__()
643
644     def test_nested(self):
645         """
646         Tests the creation of an OpenStack stack from Heat template file and
647         the retrieval of two VM instance creators and attempt to connect via
648         SSH to the first one with a floating IP.
649         """
650         created_stack = self.stack_creator.create()
651         self.assertIsNotNone(created_stack)
652
653         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
654             heat_keypair_option='private_key')
655         self.assertIsNotNone(self.vm_inst_creators)
656         self.assertEqual(1, len(self.vm_inst_creators))
657
658         for vm_inst_creator in self.vm_inst_creators:
659             self.assertTrue(
660                 create_instance_tests.validate_ssh_client(vm_inst_creator))
661
662
663 class CreateStackRouterTests(OSIntegrationTestCase):
664     """
665     Tests for the CreateStack class defined in create_stack.py where the
666     target is a Network, Subnet, and Router
667     """
668
669     def setUp(self):
670         """
671         Instantiates the CreateStack object that is responsible for downloading
672         and creating an OS stack file within OpenStack
673         """
674         self.user_roles = ['heat_stack_owner']
675
676         super(self.__class__, self).__start__()
677
678         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
679
680         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
681         self.neutron = neutron_utils.neutron_client(
682             self.os_creds, self.os_session)
683         self.stack_creator = None
684
685         self.net_name = self.guid + '-net'
686         self.subnet_name = self.guid + '-subnet'
687         self.router_name = self.guid + '-router'
688
689         self.env_values = {
690             'net_name': self.net_name,
691             'subnet_name': self.subnet_name,
692             'router_name': self.router_name,
693             'external_net_name': self.ext_net_name}
694
695         self.heat_tmplt_path = pkg_resources.resource_filename(
696             'snaps.openstack.tests.heat', 'router_heat_template.yaml')
697
698         stack_settings = StackConfig(
699             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
700             template_path=self.heat_tmplt_path,
701             env_values=self.env_values)
702         self.stack_creator = OpenStackHeatStack(
703             self.os_creds, stack_settings)
704         self.created_stack = self.stack_creator.create()
705         self.assertIsNotNone(self.created_stack)
706
707     def tearDown(self):
708         """
709         Cleans the stack and downloaded stack file
710         """
711         if self.stack_creator:
712             try:
713                 self.stack_creator.clean()
714             except:
715                 pass
716
717         super(self.__class__, self).__clean__()
718
719     def test_retrieve_router_creator(self):
720         """
721         Tests the creation of an OpenStack stack from Heat template file and
722         the retrieval of an OpenStackRouter creator/state machine instance
723         """
724         router_creators = self.stack_creator.get_router_creators()
725         self.assertEqual(1, len(router_creators))
726
727         creator = router_creators[0]
728         self.assertEqual(self.router_name, creator.router_settings.name)
729
730         router = creator.get_router()
731
732         ext_net = neutron_utils.get_network(
733             self.neutron, self.keystone, network_name=self.ext_net_name)
734         self.assertEqual(ext_net.id, router.external_network_id)
735
736
737 class CreateStackVolumeTests(OSIntegrationTestCase):
738     """
739     Tests to ensure that floating IPs can be accessed via an
740     OpenStackVolume object obtained from the OpenStackHeatStack instance
741     """
742
743     def setUp(self):
744
745         self.user_roles = ['heat_stack_owner', 'admin']
746
747         super(self.__class__, self).__start__()
748
749         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
750
751         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
752         self.stack_creator = None
753
754         self.volume_name = self.guid + '-volume'
755         self.volume_type_name = self.guid + '-volume-type'
756
757         self.env_values = {
758             'volume_name': self.volume_name,
759             'volume_type_name': self.volume_type_name}
760
761         self.heat_tmplt_path = pkg_resources.resource_filename(
762             'snaps.openstack.tests.heat', 'volume_heat_template.yaml')
763
764         stack_settings = StackConfig(
765             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
766             template_path=self.heat_tmplt_path,
767             env_values=self.env_values)
768         self.stack_creator = OpenStackHeatStack(
769             self.os_creds, stack_settings)
770         self.created_stack = self.stack_creator.create()
771         self.assertIsNotNone(self.created_stack)
772
773     def tearDown(self):
774         """
775         Cleans the stack and downloaded stack file
776         """
777         if self.stack_creator:
778             try:
779                 self.stack_creator.clean()
780             except:
781                 pass
782
783         super(self.__class__, self).__clean__()
784
785     def test_retrieve_volume_creator(self):
786         """
787         Tests the creation of an OpenStack stack from Heat template file and
788         the retrieval of an OpenStackVolume creator/state machine instance
789         """
790         volume_creators = self.stack_creator.get_volume_creators()
791         self.assertEqual(1, len(volume_creators))
792
793         creator = volume_creators[0]
794         self.assertEqual(self.volume_name, creator.volume_settings.name)
795         self.assertEqual(self.volume_name, creator.get_volume().name)
796         self.assertEqual(self.volume_type_name,
797                          creator.volume_settings.type_name)
798         self.assertEqual(self.volume_type_name, creator.get_volume().type)
799         self.assertEqual(1, creator.volume_settings.size)
800         self.assertEqual(1, creator.get_volume().size)
801
802     def test_retrieve_volume_type_creator(self):
803         """
804         Tests the creation of an OpenStack stack from Heat template file and
805         the retrieval of an OpenStackVolume creator/state machine instance
806         """
807         volume_type_creators = self.stack_creator.get_volume_type_creators()
808         self.assertEqual(1, len(volume_type_creators))
809
810         creator = volume_type_creators[0]
811         self.assertIsNotNone(creator)
812
813         volume_type = creator.get_volume_type()
814         self.assertIsNotNone(volume_type)
815
816         self.assertEqual(self.volume_type_name, volume_type.name)
817         self.assertTrue(volume_type.public)
818         self.assertIsNone(volume_type.qos_spec)
819
820         # TODO - Add encryption back and find out why it broke in Pike
821         # encryption = volume_type.encryption
822         # self.assertIsNotNone(encryption)
823         # self.assertIsNone(encryption.cipher)
824         # self.assertEqual('front-end', encryption.control_location)
825         # self.assertIsNone(encryption.key_size)
826         # self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor',
827         #                  encryption.provider)
828         # self.assertEqual(volume_type.id, encryption.volume_type_id)
829
830
831 class CreateStackFlavorTests(OSIntegrationTestCase):
832     """
833     Tests to ensure that floating IPs can be accessed via an
834     OpenStackFlavor object obtained from the OpenStackHeatStack instance
835     """
836
837     def setUp(self):
838
839         self.user_roles = ['heat_stack_owner', 'admin']
840
841         super(self.__class__, self).__start__()
842
843         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
844
845         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
846         self.stack_creator = None
847
848         self.heat_tmplt_path = pkg_resources.resource_filename(
849             'snaps.openstack.tests.heat', 'flavor_heat_template.yaml')
850
851         stack_settings = StackConfig(
852             name=self.guid + '-stack',
853             template_path=self.heat_tmplt_path)
854         self.stack_creator = OpenStackHeatStack(
855             self.os_creds, stack_settings)
856         self.created_stack = self.stack_creator.create()
857         self.assertIsNotNone(self.created_stack)
858
859     def tearDown(self):
860         """
861         Cleans the stack and downloaded stack file
862         """
863         if self.stack_creator:
864             try:
865                 self.stack_creator.clean()
866             except:
867                 pass
868
869         super(self.__class__, self).__clean__()
870
871     def test_retrieve_flavor_creator(self):
872         """
873         Tests the creation of an OpenStack stack from Heat template file and
874         the retrieval of an OpenStackVolume creator/state machine instance
875         """
876         flavor_creators = self.stack_creator.get_flavor_creators()
877         self.assertEqual(1, len(flavor_creators))
878
879         creator = flavor_creators[0]
880         self.assertTrue(creator.get_flavor().name.startswith(self.guid))
881         self.assertEqual(1024, creator.get_flavor().ram)
882         self.assertEqual(200, creator.get_flavor().disk)
883         self.assertEqual(8, creator.get_flavor().vcpus)
884         self.assertEqual(0, creator.get_flavor().ephemeral)
885         self.assertIsNone(creator.get_flavor().swap)
886         self.assertEqual(1.0, creator.get_flavor().rxtx_factor)
887         self.assertTrue(creator.get_flavor().is_public)
888
889
890 class CreateStackKeypairTests(OSIntegrationTestCase):
891     """
892     Tests to ensure that floating IPs can be accessed via an
893     OpenStackKeypair object obtained from the OpenStackHeatStack instance
894     """
895
896     def setUp(self):
897
898         self.user_roles = ['heat_stack_owner']
899
900         super(self.__class__, self).__start__()
901
902         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
903
904         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
905         self.nova = nova_utils.nova_client(self.os_creds, self.os_session)
906         self.stack_creator = None
907
908         self.keypair_name = self.guid + '-kp'
909
910         self.env_values = {
911             'keypair_name': self.keypair_name}
912
913         self.heat_tmplt_path = pkg_resources.resource_filename(
914             'snaps.openstack.tests.heat', 'keypair_heat_template.yaml')
915
916         stack_settings = StackConfig(
917             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
918             template_path=self.heat_tmplt_path,
919             env_values=self.env_values)
920         self.stack_creator = OpenStackHeatStack(
921             self.os_creds, stack_settings)
922         self.created_stack = self.stack_creator.create()
923         self.assertIsNotNone(self.created_stack)
924
925         self.keypair_creators = list()
926
927     def tearDown(self):
928         """
929         Cleans the stack and downloaded stack file
930         """
931         if self.stack_creator:
932             try:
933                 self.stack_creator.clean()
934             except:
935                 pass
936         for keypair_creator in self.keypair_creators:
937             try:
938                 keypair_creator.clean()
939             except:
940                 pass
941
942         super(self.__class__, self).__clean__()
943
944     def test_retrieve_keypair_creator(self):
945         """
946         Tests the creation of an OpenStack stack from Heat template file and
947         the retrieval of an OpenStackKeypair creator/state machine instance
948         """
949         self.kp_creators = self.stack_creator.get_keypair_creators(
950             'private_key')
951         self.assertEqual(1, len(self.kp_creators))
952
953         self.keypair_creator = self.kp_creators[0]
954
955         self.assertEqual(self.keypair_name,
956                          self.keypair_creator.get_keypair().name)
957         self.assertIsNotNone(
958             self.keypair_creator.keypair_settings.private_filepath)
959
960         private_file_contents = file_utils.read_file(
961             self.keypair_creator.keypair_settings.private_filepath)
962         self.assertTrue(private_file_contents.startswith(
963             '-----BEGIN RSA PRIVATE KEY-----'))
964
965         keypair = nova_utils.get_keypair_by_id(
966             self.nova, self.keypair_creator.get_keypair().id)
967         self.assertIsNotNone(keypair)
968         self.assertEqual(self.keypair_creator.get_keypair(), keypair)
969
970
971 class CreateStackSecurityGroupTests(OSIntegrationTestCase):
972     """
973     Tests for the OpenStackHeatStack class to ensure it returns an
974     OpenStackSecurityGroup object
975     """
976
977     def setUp(self):
978         """
979         Instantiates the CreateStack object that is responsible for downloading
980         and creating an OS stack file within OpenStack
981         """
982         self.user_roles = ['heat_stack_owner']
983
984         super(self.__class__, self).__start__()
985
986         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
987
988         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
989         self.nova = nova_utils.nova_client(self.os_creds, self.os_session)
990         self.stack_creator = None
991
992         self.security_group_name = self.guid + '-sec-grp'
993
994         self.env_values = {
995             'security_group_name': self.security_group_name}
996
997         self.heat_tmplt_path = pkg_resources.resource_filename(
998             'snaps.openstack.tests.heat', 'security_group_heat_template.yaml')
999
1000         stack_settings = StackConfig(
1001             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
1002             template_path=self.heat_tmplt_path,
1003             env_values=self.env_values)
1004         self.stack_creator = OpenStackHeatStack(
1005             self.os_creds, stack_settings)
1006         self.created_stack = self.stack_creator.create()
1007         self.assertIsNotNone(self.created_stack)
1008
1009     def tearDown(self):
1010         """
1011         Cleans the stack and downloaded stack file
1012         """
1013         if self.stack_creator:
1014             try:
1015                 self.stack_creator.clean()
1016             except:
1017                 pass
1018
1019         super(self.__class__, self).__clean__()
1020
1021     def test_retrieve_security_group_creator(self):
1022         """
1023         Tests the creation of an OpenStack stack from Heat template file and
1024         the retrieval of an OpenStackSecurityGroup creator/state machine
1025         instance
1026         """
1027         sec_grp_creators = self.stack_creator.get_security_group_creators()
1028         self.assertEqual(1, len(sec_grp_creators))
1029
1030         creator = sec_grp_creators[0]
1031         sec_grp = creator.get_security_group()
1032
1033         self.assertEqual(self.security_group_name, sec_grp.name)
1034         self.assertEqual('Test description', sec_grp.description)
1035         self.assertEqual(2, len(sec_grp.rules))
1036
1037         has_ssh_rule = False
1038         has_icmp_rule = False
1039
1040         for rule in sec_grp.rules:
1041             if (rule.security_group_id == sec_grp.id
1042                     and rule.direction == 'egress'
1043                     and rule.ethertype == 'IPv4'
1044                     and rule.port_range_min == 22
1045                     and rule.port_range_max == 22
1046                     and rule.protocol == 'tcp'
1047                     and rule.remote_group_id is None
1048                     and rule.remote_ip_prefix == '0.0.0.0/0'):
1049                 has_ssh_rule = True
1050             if (rule.security_group_id == sec_grp.id
1051                     and rule.direction == 'ingress'
1052                     and rule.ethertype == 'IPv4'
1053                     and rule.port_range_min is None
1054                     and rule.port_range_max is None
1055                     and rule.protocol == 'icmp'
1056                     and rule.remote_group_id is None
1057                     and rule.remote_ip_prefix == '0.0.0.0/0'):
1058                 has_icmp_rule = True
1059
1060         self.assertTrue(has_ssh_rule)
1061         self.assertTrue(has_icmp_rule)
1062
1063
1064 class CreateStackNegativeTests(OSIntegrationTestCase):
1065     """
1066     Negative test cases for the OpenStackHeatStack class with poor
1067     configuration
1068     """
1069
1070     def setUp(self):
1071         self.user_roles = ['heat_stack_owner']
1072
1073         super(self.__class__, self).__start__()
1074
1075         self.stack_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
1076         self.stack_creator = None
1077         self.heat_tmplt_path = pkg_resources.resource_filename(
1078             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
1079
1080     def tearDown(self):
1081         if self.stack_creator:
1082             self.stack_creator.clean()
1083
1084         super(self.__class__, self).__clean__()
1085
1086     def test_missing_dependencies(self):
1087         """
1088         Expect an StackCreationError when the stack file does not exist
1089         """
1090         stack_settings = StackConfig(name=self.stack_name,
1091                                      template_path=self.heat_tmplt_path)
1092         self.stack_creator = OpenStackHeatStack(
1093             self.os_creds, stack_settings)
1094         with self.assertRaises(HTTPBadRequest):
1095             self.stack_creator.create()
1096
1097     def test_bad_stack_file(self):
1098         """
1099         Expect an StackCreationError when the stack file does not exist
1100         """
1101         stack_settings = StackConfig(
1102             name=self.stack_name, template_path='foo')
1103         self.stack_creator = OpenStackHeatStack(
1104             self.os_creds, stack_settings)
1105         with self.assertRaises(IOError):
1106             self.stack_creator.create()
1107
1108
1109 class CreateStackFailureTests(OSIntegrationTestCase):
1110     """
1111     Tests for the OpenStackHeatStack class defined in create_stack.py for
1112     when failures occur. Failures are being triggered by allocating 1 million
1113     CPUs.
1114     """
1115
1116     def setUp(self):
1117         self.user_roles = ['heat_stack_owner']
1118
1119         super(self.__class__, self).__start__()
1120
1121         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
1122
1123         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
1124         self.stack_creator = None
1125
1126         self.tmp_file = file_utils.save_string_to_file(
1127             ' ', str(uuid.uuid4()) + '-bad-image')
1128         self.image_creator = OpenStackImage(
1129             self.os_creds, ImageConfig(
1130                 name=self.guid + 'image', image_file=self.tmp_file.name,
1131                 image_user='foo', img_format='qcow2'))
1132         self.image_creator.create()
1133
1134         # Create Flavor
1135         self.flavor_creator = OpenStackFlavor(
1136             self.admin_os_creds,
1137             FlavorConfig(
1138                 name=self.guid + '-flavor-name', ram=256, disk=10,
1139                 vcpus=1000000))
1140         self.flavor_creator.create()
1141
1142         self.network_name = self.guid + '-net'
1143         self.subnet_name = self.guid + '-subnet'
1144         self.vm_inst_name = self.guid + '-inst'
1145
1146         self.env_values = {
1147             'image_name': self.image_creator.image_settings.name,
1148             'flavor_name': self.flavor_creator.flavor_settings.name,
1149             'net_name': self.network_name,
1150             'subnet_name': self.subnet_name,
1151             'inst_name': self.vm_inst_name}
1152
1153         self.heat_tmplt_path = pkg_resources.resource_filename(
1154             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
1155
1156     def tearDown(self):
1157         """
1158         Cleans the stack and downloaded stack file
1159         """
1160         if self.stack_creator:
1161             try:
1162                 self.stack_creator.clean()
1163             except:
1164                 pass
1165
1166         if self.image_creator:
1167             try:
1168                 self.image_creator.clean()
1169             except:
1170                 pass
1171
1172         if self.flavor_creator:
1173             try:
1174                 self.flavor_creator.clean()
1175             except:
1176                 pass
1177
1178         if self.tmp_file:
1179             try:
1180                 os.remove(self.tmp_file.name)
1181             except:
1182                 pass
1183
1184         super(self.__class__, self).__clean__()
1185
1186     def test_stack_failure(self):
1187         """
1188         Tests the creation of an OpenStack stack from Heat template file that
1189         should always fail due to too many CPU cores
1190         """
1191         # Create Stack
1192         # Set the default stack settings, then set any custom parameters sent
1193         # from the app
1194         stack_settings = StackConfig(
1195             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
1196             template_path=self.heat_tmplt_path,
1197             env_values=self.env_values)
1198         self.stack_creator = OpenStackHeatStack(
1199             self.os_creds, stack_settings)
1200
1201         with self.assertRaises(StackError):
1202             try:
1203                 self.stack_creator.create()
1204             except StackError:
1205                 resources = heat_utils.get_resources(
1206                     self.heat_cli, self.stack_creator.get_stack().id)
1207
1208                 found = False
1209                 for resource in resources:
1210                     if (resource.status ==
1211                             snaps.config.stack.STATUS_CREATE_COMPLETE):
1212                         found = True
1213                 self.assertTrue(found)
1214                 raise