Merge "Return OpenStackVolume and OpenStackVolumeType instances from heat."
[snaps.git] / snaps / openstack / utils / tests / heat_utils_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 logging
16 import pkg_resources
17 import uuid
18
19 import time
20
21 from snaps.openstack import create_stack
22 from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
23
24 from snaps.openstack.create_image import OpenStackImage
25 from snaps.openstack.create_instance import OpenStackVmInstance
26 from snaps.openstack.create_stack import StackSettings
27 from snaps.openstack.tests import openstack_tests
28 from snaps.openstack.tests.os_source_file_test import OSComponentTestCase
29 from snaps.openstack.utils import (
30     heat_utils, neutron_utils, nova_utils, settings_utils, glance_utils,
31     cinder_utils)
32
33 __author__ = 'spisarski'
34
35 logger = logging.getLogger('heat_utils_tests')
36
37
38 class HeatSmokeTests(OSComponentTestCase):
39     """
40     Tests to ensure that the heat client can communicate with the cloud
41     """
42
43     def test_heat_connect_success(self):
44         """
45         Tests to ensure that the proper credentials can connect.
46         """
47         heat = heat_utils.heat_client(self.os_creds)
48
49         # This should not throw an exception
50         stacks = heat.stacks.list()
51         for stack in stacks:
52             print stack
53
54     def test_heat_connect_fail(self):
55         """
56         Tests to ensure that the improper credentials cannot connect.
57         """
58         from snaps.openstack.os_credentials import OSCreds
59
60         heat = heat_utils.heat_client(
61             OSCreds(username='user', password='pass',
62                     auth_url=self.os_creds.auth_url,
63                     project_name=self.os_creds.project_name,
64                     proxy_settings=self.os_creds.proxy_settings))
65         stacks = heat.stacks.list()
66
67         # This should throw an exception
68         with self.assertRaises(Exception):
69             for stack in stacks:
70                 print stack
71
72
73 class HeatUtilsCreateSimpleStackTests(OSComponentTestCase):
74     """
75     Test basic Heat functionality
76     """
77
78     def setUp(self):
79         """
80         Instantiates OpenStack instances that cannot be spawned by Heat
81         """
82         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
83         stack_name1 = guid + '-stack1'
84         stack_name2 = guid + '-stack2'
85         self.network_name = guid + '-net'
86         self.subnet_name = guid + '-subnet'
87         self.vm_inst_name = guid + '-inst'
88
89         self.image_creator = OpenStackImage(
90             self.os_creds, openstack_tests.cirros_image_settings(
91                 name=guid + '-image', image_metadata=self.image_metadata))
92         self.image_creator.create()
93
94         # Create Flavor
95         self.flavor_creator = OpenStackFlavor(
96             self.os_creds,
97             FlavorSettings(name=guid + '-flavor', ram=256, disk=10, vcpus=1))
98         self.flavor_creator.create()
99
100         env_values = {'image_name': self.image_creator.image_settings.name,
101                       'flavor_name': self.flavor_creator.flavor_settings.name,
102                       'net_name': self.network_name,
103                       'subnet_name': self.subnet_name,
104                       'inst_name': self.vm_inst_name}
105         heat_tmplt_path = pkg_resources.resource_filename(
106             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
107         self.stack_settings1 = StackSettings(
108             name=stack_name1, template_path=heat_tmplt_path,
109             env_values=env_values)
110         self.stack_settings2 = StackSettings(
111             name=stack_name2, template_path=heat_tmplt_path,
112             env_values=env_values)
113         self.stack1 = None
114         self.stack2 = None
115         self.heat_client = heat_utils.heat_client(self.os_creds)
116
117     def tearDown(self):
118         """
119         Cleans the image and downloaded image file
120         """
121         if self.stack1:
122             try:
123                 heat_utils.delete_stack(self.heat_client, self.stack1)
124             except:
125                 pass
126
127         if self.stack2:
128             try:
129                 heat_utils.delete_stack(self.heat_client, self.stack2)
130             except:
131                 pass
132
133         if self.image_creator:
134             try:
135                 self.image_creator.clean()
136             except:
137                 pass
138
139         if self.flavor_creator:
140             try:
141                 self.flavor_creator.clean()
142             except:
143                 pass
144
145     def test_create_stack(self):
146         """
147         Tests the creation of an OpenStack Heat stack1 that does not exist.
148         """
149         self.stack1 = heat_utils.create_stack(self.heat_client,
150                                               self.stack_settings1)
151
152         stack_query_1 = heat_utils.get_stack(
153             self.heat_client, stack_settings=self.stack_settings1)
154         self.assertEqual(self.stack1, stack_query_1)
155
156         stack_query_2 = heat_utils.get_stack(
157             self.heat_client, stack_name=self.stack_settings1.name)
158         self.assertEqual(self.stack1, stack_query_2)
159
160         stack_query_3 = heat_utils.get_stack_by_id(self.heat_client,
161                                                    self.stack1.id)
162         self.assertEqual(self.stack1, stack_query_3)
163
164         resources = heat_utils.get_resources(self.heat_client, self.stack1)
165         self.assertIsNotNone(resources)
166         self.assertEqual(4, len(resources))
167
168         outputs = heat_utils.get_outputs(self.heat_client, self.stack1)
169         self.assertIsNotNone(outputs)
170         self.assertEqual(0, len(outputs))
171
172         # Wait until stack deployment has completed
173         end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
174         is_active = False
175         while time.time() < end_time:
176             status = heat_utils.get_stack_status(self.heat_client,
177                                                  self.stack1.id)
178             if status == create_stack.STATUS_CREATE_COMPLETE:
179                 is_active = True
180                 break
181             elif status == create_stack.STATUS_CREATE_FAILED:
182                 is_active = False
183                 break
184
185             time.sleep(3)
186
187         self.assertTrue(is_active)
188
189         neutron = neutron_utils.neutron_client(self.os_creds)
190         networks = heat_utils.get_stack_networks(
191             self.heat_client, neutron, self.stack1)
192         self.assertIsNotNone(networks)
193         self.assertEqual(1, len(networks))
194         self.assertEqual(self.network_name, networks[0].name)
195
196         subnets = neutron_utils.get_subnets_by_network(neutron, networks[0])
197         self.assertEqual(1, len(subnets))
198         self.assertEqual(self.subnet_name, subnets[0].name)
199
200         nova = nova_utils.nova_client(self.os_creds)
201         servers = heat_utils.get_stack_servers(
202             self.heat_client, nova, self.stack1)
203         self.assertIsNotNone(servers)
204         self.assertEqual(1, len(servers))
205         self.assertEqual(self.vm_inst_name, servers[0].name)
206
207     def test_create_stack_x2(self):
208         """
209         Tests the creation of an OpenStack keypair that does not exist.
210         """
211         self.stack1 = heat_utils.create_stack(self.heat_client,
212                                               self.stack_settings1)
213
214         stack1_query_1 = heat_utils.get_stack(
215             self.heat_client, stack_settings=self.stack_settings1)
216         self.assertEqual(self.stack1, stack1_query_1)
217
218         stack1_query_2 = heat_utils.get_stack(
219             self.heat_client, stack_name=self.stack_settings1.name)
220         self.assertEqual(self.stack1, stack1_query_2)
221
222         stack1_query_3 = heat_utils.get_stack_by_id(self.heat_client,
223                                                     self.stack1.id)
224         self.assertEqual(self.stack1, stack1_query_3)
225
226         end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
227         is_active = False
228         while time.time() < end_time:
229             status = heat_utils.get_stack_status(self.heat_client,
230                                                  self.stack1.id)
231             if status == create_stack.STATUS_CREATE_COMPLETE:
232                 is_active = True
233                 break
234             elif status == create_stack.STATUS_CREATE_FAILED:
235                 is_active = False
236                 break
237
238             time.sleep(3)
239
240         self.assertTrue(is_active)
241
242         self.stack2 = heat_utils.create_stack(self.heat_client,
243                                               self.stack_settings2)
244
245         stack2_query_1 = heat_utils.get_stack(
246             self.heat_client, stack_settings=self.stack_settings2)
247         self.assertEqual(self.stack2, stack2_query_1)
248
249         stack2_query_2 = heat_utils.get_stack(
250             self.heat_client, stack_name=self.stack_settings2.name)
251         self.assertEqual(self.stack2, stack2_query_2)
252
253         stack2_query_3 = heat_utils.get_stack_by_id(self.heat_client,
254                                                     self.stack2.id)
255         self.assertEqual(self.stack2, stack2_query_3)
256
257         end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
258
259         is_active = False
260         while time.time() < end_time:
261             status = heat_utils.get_stack_status(self.heat_client,
262                                                  self.stack2.id)
263             if status == create_stack.STATUS_CREATE_COMPLETE:
264                 is_active = True
265                 break
266             elif status == create_stack.STATUS_CREATE_FAILED:
267                 is_active = False
268                 break
269
270             time.sleep(3)
271
272         self.assertTrue(is_active)
273
274
275 class HeatUtilsCreateComplexStackTests(OSComponentTestCase):
276     """
277     Test basic Heat functionality
278     """
279
280     def setUp(self):
281         """
282         Instantiates OpenStack instances that cannot be spawned by Heat
283         """
284         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
285         stack_name = guid + '-stack'
286         self.network_name = guid + '-net'
287         self.subnet_name = guid + '-subnet'
288         self.vm_inst1_name = guid + '-inst1'
289         self.vm_inst2_name = guid + '-inst2'
290         self.flavor1_name = guid + '-flavor1'
291         self.flavor2_name = guid + '-flavor2'
292         self.keypair_name = guid + '-keypair'
293
294         self.image_creator1 = OpenStackImage(
295             self.os_creds, openstack_tests.cirros_image_settings(
296                 name=guid + '-image1', image_metadata=self.image_metadata))
297         self.image_creator1.create()
298
299         self.image_creator2 = OpenStackImage(
300             self.os_creds, openstack_tests.cirros_image_settings(
301                 name=guid + '-image2', image_metadata=self.image_metadata))
302         self.image_creator2.create()
303
304         env_values = {'image1_name': self.image_creator1.image_settings.name,
305                       'image2_name': self.image_creator2.image_settings.name,
306                       'flavor1_name': self.flavor1_name,
307                       'flavor2_name': self.flavor2_name,
308                       'net_name': self.network_name,
309                       'subnet_name': self.subnet_name,
310                       'keypair_name': self.keypair_name,
311                       'inst1_name': self.vm_inst1_name,
312                       'inst2_name': self.vm_inst2_name,
313                       'external_net_name': self.ext_net_name}
314         heat_tmplt_path = pkg_resources.resource_filename(
315             'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
316         stack_settings = StackSettings(
317             name=stack_name, template_path=heat_tmplt_path,
318             env_values=env_values)
319         self.heat_client = heat_utils.heat_client(self.os_creds)
320         self.stack = heat_utils.create_stack(self.heat_client, stack_settings)
321
322         # Wait until stack deployment has completed
323         end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
324         is_active = False
325         while time.time() < end_time:
326             status = heat_utils.get_stack_status(self.heat_client,
327                                                  self.stack.id)
328             if status == create_stack.STATUS_CREATE_COMPLETE:
329                 is_active = True
330                 break
331             elif status == create_stack.STATUS_CREATE_FAILED:
332                 is_active = False
333                 break
334
335             time.sleep(3)
336         self.assertTrue(is_active)
337
338     def tearDown(self):
339         """
340         Cleans the image and downloaded image file
341         """
342         if self.stack:
343             try:
344                 heat_utils.delete_stack(self.heat_client, self.stack)
345                 # Wait until stack deployment has completed
346                 end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
347                 is_deleted = False
348                 while time.time() < end_time:
349                     status = heat_utils.get_stack_status(self.heat_client,
350                                                          self.stack.id)
351                     if status == create_stack.STATUS_DELETE_COMPLETE:
352                         is_deleted = True
353                         break
354                     elif status == create_stack.STATUS_DELETE_FAILED:
355                         is_deleted = False
356                         break
357
358                     time.sleep(3)
359
360                 if not is_deleted:
361                     nova = nova_utils.nova_client(self.os_creds)
362                     neutron = neutron_utils.neutron_client(self.os_creds)
363                     glance = glance_utils.glance_client(self.os_creds)
364                     servers = heat_utils.get_stack_servers(
365                         self.heat_client, nova, self.stack)
366                     for server in servers:
367                         vm_settings = settings_utils.create_vm_inst_settings(
368                             nova, neutron, server)
369                         img_settings = settings_utils.determine_image_settings(
370                             glance, server,
371                             [self.image_creator1.image_settings,
372                              self.image_creator2.image_settings])
373                         vm_creator = OpenStackVmInstance(
374                             self.os_creds, vm_settings, img_settings)
375                         vm_creator.initialize()
376                         vm_creator.clean()
377                         vm_creator.vm_deleted(block=True)
378
379                     heat_utils.delete_stack(self.heat_client, self.stack)
380                     time.sleep(20)
381             except:
382                     raise
383
384         if self.image_creator1:
385             try:
386                 self.image_creator1.clean()
387             except:
388                 pass
389
390         if self.image_creator2:
391             try:
392                 self.image_creator2.clean()
393             except:
394                 pass
395
396     def test_get_settings_from_stack(self):
397         """
398         Tests that a heat template with floating IPs and can have the proper
399         settings derived from settings_utils.py.
400         """
401         resources = heat_utils.get_resources(self.heat_client, self.stack)
402         self.assertIsNotNone(resources)
403         self.assertEqual(12, len(resources))
404
405         options = heat_utils.get_outputs(self.heat_client, self.stack)
406         self.assertIsNotNone(options)
407         self.assertEqual(1, len(options))
408
409         neutron = neutron_utils.neutron_client(self.os_creds)
410         networks = heat_utils.get_stack_networks(
411             self.heat_client, neutron, self.stack)
412         self.assertIsNotNone(networks)
413         self.assertEqual(1, len(networks))
414         self.assertEqual(self.network_name, networks[0].name)
415
416         network_settings = settings_utils.create_network_settings(
417             neutron, networks[0])
418         self.assertIsNotNone(network_settings)
419         self.assertEqual(self.network_name, network_settings.name)
420
421         nova = nova_utils.nova_client(self.os_creds)
422         glance = glance_utils.glance_client(self.os_creds)
423
424         servers = heat_utils.get_stack_servers(
425             self.heat_client, nova, self.stack)
426         self.assertIsNotNone(servers)
427         self.assertEqual(2, len(servers))
428
429         image_settings = settings_utils.determine_image_settings(
430             glance, servers[0],
431             [self.image_creator1.image_settings,
432              self.image_creator2.image_settings])
433
434         self.assertIsNotNone(image_settings)
435         if image_settings.name.endswith('1'):
436             self.assertEqual(
437                 self.image_creator1.image_settings.name, image_settings.name)
438         else:
439             self.assertEqual(
440                 self.image_creator2.image_settings.name, image_settings.name)
441
442         image_settings = settings_utils.determine_image_settings(
443             glance, servers[1],
444             [self.image_creator1.image_settings,
445              self.image_creator2.image_settings])
446         if image_settings.name.endswith('1'):
447             self.assertEqual(
448                 self.image_creator1.image_settings.name, image_settings.name)
449         else:
450             self.assertEqual(
451                 self.image_creator2.image_settings.name, image_settings.name)
452
453         keypair1_settings = settings_utils.determine_keypair_settings(
454             self.heat_client, self.stack, servers[0],
455             priv_key_key='private_key')
456         self.assertIsNotNone(keypair1_settings)
457         self.assertEqual(self.keypair_name, keypair1_settings.name)
458
459         keypair2_settings = settings_utils.determine_keypair_settings(
460             self.heat_client, self.stack, servers[1],
461             priv_key_key='private_key')
462         self.assertIsNotNone(keypair2_settings)
463         self.assertEqual(self.keypair_name, keypair2_settings.name)
464
465
466 class HeatUtilsVolumeTests(OSComponentTestCase):
467     """
468     Test Heat volume functionality
469     """
470
471     def setUp(self):
472         """
473         Instantiates OpenStack instances that cannot be spawned by Heat
474         """
475         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
476         stack_name = guid + '-stack'
477         self.volume_name = guid + '-vol'
478         self.volume_type_name = guid + '-vol-type'
479
480         env_values = {
481             'volume_name': self.volume_name,
482             'volume_type_name': self.volume_type_name}
483
484         heat_tmplt_path = pkg_resources.resource_filename(
485             'snaps.openstack.tests.heat', 'volume_heat_template.yaml')
486         self.stack_settings = StackSettings(
487             name=stack_name, template_path=heat_tmplt_path,
488             env_values=env_values)
489         self.stack = None
490         self.heat_client = heat_utils.heat_client(self.os_creds)
491         self.cinder = cinder_utils.cinder_client(self.os_creds)
492
493     def tearDown(self):
494         """
495         Cleans the image and downloaded image file
496         """
497         if self.stack:
498             try:
499                 heat_utils.delete_stack(self.heat_client, self.stack)
500             except:
501                 pass
502
503     def test_create_vol_with_stack(self):
504         """
505         Tests the creation of an OpenStack volume with Heat.
506         """
507         self.stack = heat_utils.create_stack(
508             self.heat_client, self.stack_settings)
509
510         # Wait until stack deployment has completed
511         end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
512         is_active = False
513         while time.time() < end_time:
514             status = heat_utils.get_stack_status(self.heat_client,
515                                                  self.stack.id)
516             if status == create_stack.STATUS_CREATE_COMPLETE:
517                 is_active = True
518                 break
519             elif status == create_stack.STATUS_CREATE_FAILED:
520                 is_active = False
521                 break
522
523             time.sleep(3)
524
525         self.assertTrue(is_active)
526
527         volumes = heat_utils.get_stack_volumes(
528             self.heat_client, self.cinder, self.stack)
529
530         self.assertEqual(1, len(volumes))
531
532         volume = volumes[0]
533         self.assertEqual(self.volume_name, volume.name)
534         self.assertEqual(self.volume_type_name, volume.type)
535         self.assertEqual(1, volume.size)
536         self.assertEqual(False, volume.multi_attach)
537
538     def test_create_vol_types_with_stack(self):
539         """
540         Tests the creation of an OpenStack volume with Heat.
541         """
542         self.stack = heat_utils.create_stack(
543             self.heat_client, self.stack_settings)
544
545         # Wait until stack deployment has completed
546         end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
547         is_active = False
548         while time.time() < end_time:
549             status = heat_utils.get_stack_status(self.heat_client,
550                                                  self.stack.id)
551             if status == create_stack.STATUS_CREATE_COMPLETE:
552                 is_active = True
553                 break
554             elif status == create_stack.STATUS_CREATE_FAILED:
555                 is_active = False
556                 break
557
558             time.sleep(3)
559
560         self.assertTrue(is_active)
561
562         volume_types = heat_utils.get_stack_volume_types(
563             self.heat_client, self.cinder, self.stack)
564
565         self.assertEqual(1, len(volume_types))
566
567         volume_type = volume_types[0]
568
569         self.assertEqual(self.volume_type_name, volume_type.name)
570         self.assertTrue(volume_type.public)
571         self.assertIsNone(volume_type.qos_spec)
572
573         encryption = volume_type.encryption
574         self.assertIsNotNone(encryption)
575         self.assertIsNone(encryption.cipher)
576         self.assertEqual('front-end', encryption.control_location)
577         self.assertIsNone(encryption.key_size)
578         self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor',
579                          encryption.provider)
580         self.assertEqual(volume_type.id, encryption.volume_type_id)