1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 # and others. All rights reserved.
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:
8 # http://www.apache.org/licenses/LICENSE-2.0
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.
21 from snaps.openstack import create_stack
22 from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
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,
33 __author__ = 'spisarski'
35 logger = logging.getLogger('heat_utils_tests')
38 class HeatSmokeTests(OSComponentTestCase):
40 Tests to ensure that the heat client can communicate with the cloud
43 def test_heat_connect_success(self):
45 Tests to ensure that the proper credentials can connect.
47 heat = heat_utils.heat_client(self.os_creds)
49 # This should not throw an exception
50 stacks = heat.stacks.list()
54 def test_heat_connect_fail(self):
56 Tests to ensure that the improper credentials cannot connect.
58 from snaps.openstack.os_credentials import OSCreds
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()
67 # This should throw an exception
68 with self.assertRaises(Exception):
73 class HeatUtilsCreateSimpleStackTests(OSComponentTestCase):
75 Test basic Heat functionality
80 Instantiates OpenStack instances that cannot be spawned by Heat
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'
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()
95 self.flavor_creator = OpenStackFlavor(
97 FlavorSettings(name=guid + '-flavor', ram=256, disk=10, vcpus=1))
98 self.flavor_creator.create()
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)
115 self.heat_client = heat_utils.heat_client(self.os_creds)
119 Cleans the stack and image
123 heat_utils.delete_stack(self.heat_client, self.stack1)
129 heat_utils.delete_stack(self.heat_client, self.stack2)
133 if self.image_creator:
135 self.image_creator.clean()
139 if self.flavor_creator:
141 self.flavor_creator.clean()
145 def test_create_stack(self):
147 Tests the creation of an OpenStack Heat stack1 that does not exist.
149 self.stack1 = heat_utils.create_stack(self.heat_client,
150 self.stack_settings1)
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)
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)
160 stack_query_3 = heat_utils.get_stack_by_id(self.heat_client,
162 self.assertEqual(self.stack1, stack_query_3)
164 resources = heat_utils.get_resources(self.heat_client, self.stack1)
165 self.assertIsNotNone(resources)
166 self.assertEqual(4, len(resources))
168 outputs = heat_utils.get_outputs(self.heat_client, self.stack1)
169 self.assertIsNotNone(outputs)
170 self.assertEqual(0, len(outputs))
172 self.assertTrue(stack_active(self.heat_client, self.stack1))
174 neutron = neutron_utils.neutron_client(self.os_creds)
175 networks = heat_utils.get_stack_networks(
176 self.heat_client, neutron, self.stack1)
177 self.assertIsNotNone(networks)
178 self.assertEqual(1, len(networks))
179 self.assertEqual(self.network_name, networks[0].name)
181 subnets = neutron_utils.get_subnets_by_network(neutron, networks[0])
182 self.assertEqual(1, len(subnets))
183 self.assertEqual(self.subnet_name, subnets[0].name)
185 nova = nova_utils.nova_client(self.os_creds)
186 servers = heat_utils.get_stack_servers(
187 self.heat_client, nova, self.stack1)
188 self.assertIsNotNone(servers)
189 self.assertEqual(1, len(servers))
190 self.assertEqual(self.vm_inst_name, servers[0].name)
192 def test_create_stack_x2(self):
194 Tests the creation of an OpenStack keypair that does not exist.
196 self.stack1 = heat_utils.create_stack(self.heat_client,
197 self.stack_settings1)
199 stack1_query_1 = heat_utils.get_stack(
200 self.heat_client, stack_settings=self.stack_settings1)
201 self.assertEqual(self.stack1, stack1_query_1)
203 stack1_query_2 = heat_utils.get_stack(
204 self.heat_client, stack_name=self.stack_settings1.name)
205 self.assertEqual(self.stack1, stack1_query_2)
207 stack1_query_3 = heat_utils.get_stack_by_id(self.heat_client,
209 self.assertEqual(self.stack1, stack1_query_3)
211 self.assertTrue(stack_active(self.heat_client, self.stack1))
213 self.stack2 = heat_utils.create_stack(self.heat_client,
214 self.stack_settings2)
216 stack2_query_1 = heat_utils.get_stack(
217 self.heat_client, stack_settings=self.stack_settings2)
218 self.assertEqual(self.stack2, stack2_query_1)
220 stack2_query_2 = heat_utils.get_stack(
221 self.heat_client, stack_name=self.stack_settings2.name)
222 self.assertEqual(self.stack2, stack2_query_2)
224 stack2_query_3 = heat_utils.get_stack_by_id(self.heat_client,
226 self.assertEqual(self.stack2, stack2_query_3)
228 self.assertTrue(stack_active(self.heat_client, self.stack2))
231 class HeatUtilsCreateComplexStackTests(OSComponentTestCase):
233 Test basic Heat functionality
238 Instantiates OpenStack instances that cannot be spawned by Heat
240 guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
241 stack_name = guid + '-stack'
242 self.network_name = guid + '-net'
243 self.subnet_name = guid + '-subnet'
244 self.vm_inst1_name = guid + '-inst1'
245 self.vm_inst2_name = guid + '-inst2'
246 self.flavor1_name = guid + '-flavor1'
247 self.flavor2_name = guid + '-flavor2'
248 self.keypair_name = guid + '-keypair'
250 self.image_creator1 = OpenStackImage(
251 self.os_creds, openstack_tests.cirros_image_settings(
252 name=guid + '-image1', image_metadata=self.image_metadata))
253 self.image_creator1.create()
255 self.image_creator2 = OpenStackImage(
256 self.os_creds, openstack_tests.cirros_image_settings(
257 name=guid + '-image2', image_metadata=self.image_metadata))
258 self.image_creator2.create()
260 env_values = {'image1_name': self.image_creator1.image_settings.name,
261 'image2_name': self.image_creator2.image_settings.name,
262 'flavor1_name': self.flavor1_name,
263 'flavor2_name': self.flavor2_name,
264 'net_name': self.network_name,
265 'subnet_name': self.subnet_name,
266 'keypair_name': self.keypair_name,
267 'inst1_name': self.vm_inst1_name,
268 'inst2_name': self.vm_inst2_name,
269 'external_net_name': self.ext_net_name}
270 heat_tmplt_path = pkg_resources.resource_filename(
271 'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
272 stack_settings = StackSettings(
273 name=stack_name, template_path=heat_tmplt_path,
274 env_values=env_values)
275 self.heat_client = heat_utils.heat_client(self.os_creds)
276 self.stack = heat_utils.create_stack(self.heat_client, stack_settings)
278 self.assertTrue(stack_active(self.heat_client, self.stack))
282 Cleans the stack and image
286 heat_utils.delete_stack(self.heat_client, self.stack)
287 # Wait until stack deployment has completed
288 end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
290 while time.time() < end_time:
291 status = heat_utils.get_stack_status(self.heat_client,
293 if status == create_stack.STATUS_DELETE_COMPLETE:
296 elif status == create_stack.STATUS_DELETE_FAILED:
303 nova = nova_utils.nova_client(self.os_creds)
304 neutron = neutron_utils.neutron_client(self.os_creds)
305 glance = glance_utils.glance_client(self.os_creds)
306 servers = heat_utils.get_stack_servers(
307 self.heat_client, nova, self.stack)
308 for server in servers:
309 vm_settings = settings_utils.create_vm_inst_settings(
310 nova, neutron, server)
311 img_settings = settings_utils.determine_image_settings(
313 [self.image_creator1.image_settings,
314 self.image_creator2.image_settings])
315 vm_creator = OpenStackVmInstance(
316 self.os_creds, vm_settings, img_settings)
317 vm_creator.initialize()
319 vm_creator.vm_deleted(block=True)
321 heat_utils.delete_stack(self.heat_client, self.stack)
326 if self.image_creator1:
328 self.image_creator1.clean()
332 if self.image_creator2:
334 self.image_creator2.clean()
338 def test_get_settings_from_stack(self):
340 Tests that a heat template with floating IPs and can have the proper
341 settings derived from settings_utils.py.
343 resources = heat_utils.get_resources(self.heat_client, self.stack)
344 self.assertIsNotNone(resources)
345 self.assertEqual(12, len(resources))
347 options = heat_utils.get_outputs(self.heat_client, self.stack)
348 self.assertIsNotNone(options)
349 self.assertEqual(1, len(options))
351 neutron = neutron_utils.neutron_client(self.os_creds)
352 networks = heat_utils.get_stack_networks(
353 self.heat_client, neutron, self.stack)
354 self.assertIsNotNone(networks)
355 self.assertEqual(1, len(networks))
356 self.assertEqual(self.network_name, networks[0].name)
358 network_settings = settings_utils.create_network_settings(
359 neutron, networks[0])
360 self.assertIsNotNone(network_settings)
361 self.assertEqual(self.network_name, network_settings.name)
363 nova = nova_utils.nova_client(self.os_creds)
364 glance = glance_utils.glance_client(self.os_creds)
366 servers = heat_utils.get_stack_servers(
367 self.heat_client, nova, self.stack)
368 self.assertIsNotNone(servers)
369 self.assertEqual(2, len(servers))
371 image_settings = settings_utils.determine_image_settings(
373 [self.image_creator1.image_settings,
374 self.image_creator2.image_settings])
376 self.assertIsNotNone(image_settings)
377 if image_settings.name.endswith('1'):
379 self.image_creator1.image_settings.name, image_settings.name)
382 self.image_creator2.image_settings.name, image_settings.name)
384 image_settings = settings_utils.determine_image_settings(
386 [self.image_creator1.image_settings,
387 self.image_creator2.image_settings])
388 if image_settings.name.endswith('1'):
390 self.image_creator1.image_settings.name, image_settings.name)
393 self.image_creator2.image_settings.name, image_settings.name)
395 keypair1_settings = settings_utils.determine_keypair_settings(
396 self.heat_client, self.stack, servers[0],
397 priv_key_key='private_key')
398 self.assertIsNotNone(keypair1_settings)
399 self.assertEqual(self.keypair_name, keypair1_settings.name)
401 keypair2_settings = settings_utils.determine_keypair_settings(
402 self.heat_client, self.stack, servers[1],
403 priv_key_key='private_key')
404 self.assertIsNotNone(keypair2_settings)
405 self.assertEqual(self.keypair_name, keypair2_settings.name)
408 class HeatUtilsRouterTests(OSComponentTestCase):
410 Test Heat volume functionality
415 Instantiates OpenStack instances that cannot be spawned by Heat
417 guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
418 stack_name = guid + '-stack'
420 self.net_name = guid + '-net'
421 self.subnet_name = guid + '-subnet'
422 self.router_name = guid + '-router'
425 'net_name': self.net_name,
426 'subnet_name': self.subnet_name,
427 'router_name': self.router_name,
428 'external_net_name': self.ext_net_name}
430 heat_tmplt_path = pkg_resources.resource_filename(
431 'snaps.openstack.tests.heat', 'router_heat_template.yaml')
432 self.stack_settings = StackSettings(
433 name=stack_name, template_path=heat_tmplt_path,
434 env_values=env_values)
436 self.heat_client = heat_utils.heat_client(self.os_creds)
437 self.neutron = neutron_utils.neutron_client(self.os_creds)
441 Cleans the image and downloaded image file
445 heat_utils.delete_stack(self.heat_client, self.stack)
449 def test_create_router_with_stack(self):
451 Tests the creation of an OpenStack router with Heat and the retrieval
452 of the Router Domain objects from heat_utils#get_stack_routers().
454 self.stack = heat_utils.create_stack(
455 self.heat_client, self.stack_settings)
457 # Wait until stack deployment has completed
458 end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
460 while time.time() < end_time:
461 status = heat_utils.get_stack_status(self.heat_client,
463 if status == create_stack.STATUS_CREATE_COMPLETE:
466 elif status == create_stack.STATUS_CREATE_FAILED:
472 self.assertTrue(is_active)
474 routers = heat_utils.get_stack_routers(
475 self.heat_client, self.neutron, self.stack)
477 self.assertEqual(1, len(routers))
480 self.assertEqual(self.router_name, router.name)
482 ext_net = neutron_utils.get_network(
483 self.neutron, network_name=self.ext_net_name)
484 self.assertEqual(ext_net.id, router.external_network_id)
487 class HeatUtilsVolumeTests(OSComponentTestCase):
489 Test Heat volume functionality
494 Instantiates OpenStack instances that cannot be spawned by Heat
496 guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
497 stack_name = guid + '-stack'
498 self.volume_name = guid + '-vol'
499 self.volume_type_name = guid + '-vol-type'
502 'volume_name': self.volume_name,
503 'volume_type_name': self.volume_type_name}
505 heat_tmplt_path = pkg_resources.resource_filename(
506 'snaps.openstack.tests.heat', 'volume_heat_template.yaml')
507 self.stack_settings = StackSettings(
508 name=stack_name, template_path=heat_tmplt_path,
509 env_values=env_values)
511 self.heat_client = heat_utils.heat_client(self.os_creds)
512 self.cinder = cinder_utils.cinder_client(self.os_creds)
520 heat_utils.delete_stack(self.heat_client, self.stack)
524 def test_create_vol_with_stack(self):
526 Tests the creation of an OpenStack volume with Heat.
528 self.stack = heat_utils.create_stack(
529 self.heat_client, self.stack_settings)
530 self.assertTrue(stack_active(self.heat_client, self.stack))
532 volumes = heat_utils.get_stack_volumes(
533 self.heat_client, self.cinder, self.stack)
535 self.assertEqual(1, len(volumes))
538 self.assertEqual(self.volume_name, volume.name)
539 self.assertEqual(self.volume_type_name, volume.type)
540 self.assertEqual(1, volume.size)
541 self.assertEqual(False, volume.multi_attach)
543 def test_create_vol_types_with_stack(self):
545 Tests the creation of an OpenStack volume with Heat.
547 self.stack = heat_utils.create_stack(
548 self.heat_client, self.stack_settings)
549 self.assertTrue(stack_active(self.heat_client, self.stack))
551 volume_types = heat_utils.get_stack_volume_types(
552 self.heat_client, self.cinder, self.stack)
554 self.assertEqual(1, len(volume_types))
556 volume_type = volume_types[0]
558 self.assertEqual(self.volume_type_name, volume_type.name)
559 self.assertTrue(volume_type.public)
560 self.assertIsNone(volume_type.qos_spec)
562 encryption = volume_type.encryption
563 self.assertIsNotNone(encryption)
564 self.assertIsNone(encryption.cipher)
565 self.assertEqual('front-end', encryption.control_location)
566 self.assertIsNone(encryption.key_size)
567 self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor',
569 self.assertEqual(volume_type.id, encryption.volume_type_id)
572 class HeatUtilsFlavorTests(OSComponentTestCase):
574 Test Heat volume functionality
579 Instantiates OpenStack instances that cannot be spawned by Heat
581 guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
582 self.name_prefix = guid
583 stack_name = guid + '-stack'
585 heat_tmplt_path = pkg_resources.resource_filename(
586 'snaps.openstack.tests.heat', 'flavor_heat_template.yaml')
587 self.stack_settings = StackSettings(
588 name=stack_name, template_path=heat_tmplt_path)
590 self.heat_client = heat_utils.heat_client(self.os_creds)
591 self.nova = nova_utils.nova_client(self.os_creds)
599 heat_utils.delete_stack(self.heat_client, self.stack)
603 def test_create_flavor_with_stack(self):
605 Tests the creation of an OpenStack volume with Heat.
607 self.stack = heat_utils.create_stack(
608 self.heat_client, self.stack_settings)
610 self.assertTrue(stack_active(self.heat_client, self.stack))
612 flavors = heat_utils.get_stack_flavors(
613 self.heat_client, self.nova, self.stack)
615 self.assertEqual(1, len(flavors))
618 self.assertTrue(flavor.name.startswith(self.name_prefix))
619 self.assertEqual(1024, flavor.ram)
620 self.assertEqual(200, flavor.disk)
621 self.assertEqual(8, flavor.vcpus)
622 self.assertEqual(0, flavor.ephemeral)
623 self.assertIsNone(flavor.swap)
624 self.assertEqual(1.0, flavor.rxtx_factor)
625 self.assertTrue(flavor.is_public)
628 class HeatUtilsKeypairTests(OSComponentTestCase):
630 Test Heat volume functionality
635 Instantiates OpenStack instances that cannot be spawned by Heat
637 guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
638 stack_name = guid + '-stack'
639 self.keypair_name = guid + '-kp'
641 env_values = {'keypair_name': self.keypair_name}
643 heat_tmplt_path = pkg_resources.resource_filename(
644 'snaps.openstack.tests.heat', 'keypair_heat_template.yaml')
645 self.stack_settings = StackSettings(
646 name=stack_name, template_path=heat_tmplt_path,
647 env_values=env_values)
649 self.heat_client = heat_utils.heat_client(self.os_creds)
650 self.nova = nova_utils.nova_client(self.os_creds)
658 heat_utils.delete_stack(self.heat_client, self.stack)
662 def test_create_keypair_with_stack(self):
664 Tests the creation of an OpenStack keypair with Heat.
666 self.stack = heat_utils.create_stack(
667 self.heat_client, self.stack_settings)
668 self.assertTrue(stack_active(self.heat_client, self.stack))
670 keypairs = heat_utils.get_stack_keypairs(
671 self.heat_client, self.nova, self.stack)
673 self.assertEqual(1, len(keypairs))
674 keypair = keypairs[0]
676 self.assertEqual(self.keypair_name, keypair.name)
678 outputs = heat_utils.get_outputs(self.heat_client, self.stack)
680 for output in outputs:
681 if output.key == 'private_key':
682 self.assertTrue(output.value.startswith(
683 '-----BEGIN RSA PRIVATE KEY-----'))
685 keypair = nova_utils.get_keypair_by_id(self.nova, keypair.id)
686 self.assertIsNotNone(keypair)
688 self.assertEqual(self.keypair_name, keypair.name)
691 class HeatUtilsSecurityGroupTests(OSComponentTestCase):
693 Test Heat volume functionality
698 Instantiates OpenStack instances that cannot be spawned by Heat
700 guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
701 stack_name = guid + '-stack'
702 self.sec_grp_name = guid + '-sec-grp'
704 env_values = {'security_group_name': self.sec_grp_name}
706 heat_tmplt_path = pkg_resources.resource_filename(
707 'snaps.openstack.tests.heat', 'security_group_heat_template.yaml')
708 self.stack_settings = StackSettings(
709 name=stack_name, template_path=heat_tmplt_path,
710 env_values=env_values)
712 self.heat_client = heat_utils.heat_client(self.os_creds)
713 self.neutron = neutron_utils.neutron_client(self.os_creds)
721 heat_utils.delete_stack(self.heat_client, self.stack)
725 def test_create_security_group_with_stack(self):
727 Tests the creation of an OpenStack SecurityGroup with Heat.
729 self.stack = heat_utils.create_stack(
730 self.heat_client, self.stack_settings)
731 self.assertTrue(stack_active(self.heat_client, self.stack))
733 sec_grp = heat_utils.get_stack_security_groups(
734 self.heat_client, self.neutron, self.stack)[0]
736 self.assertEqual(self.sec_grp_name, sec_grp.name)
737 self.assertEqual('Test description', sec_grp.description)
738 self.assertEqual(2, len(sec_grp.rules))
741 has_icmp_rule = False
743 for rule in sec_grp.rules:
744 if (rule.security_group_id == sec_grp.id
745 and rule.direction == 'egress'
746 and rule.ethertype == 'IPv4'
747 and rule.port_range_min == 22
748 and rule.port_range_max == 22
749 and rule.protocol == 'tcp'
750 and rule.remote_group_id is None
751 and rule.remote_ip_prefix == '0.0.0.0/0'):
753 if (rule.security_group_id == sec_grp.id
754 and rule.direction == 'ingress'
755 and rule.ethertype == 'IPv4'
756 and rule.port_range_min is None
757 and rule.port_range_max is None
758 and rule.protocol == 'icmp'
759 and rule.remote_group_id is None
760 and rule.remote_ip_prefix == '0.0.0.0/0'):
763 self.assertTrue(has_ssh_rule)
764 self.assertTrue(has_icmp_rule)
767 def stack_active(heat_cli, stack):
769 Blocks until stack application has successfully completed or failed
770 :param heat_cli: the Heat client
771 :param stack: the Stack domain object
774 # Wait until stack deployment has completed
775 end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
777 while time.time() < end_time:
778 status = heat_utils.get_stack_status(heat_cli, stack.id)
779 if status == create_stack.STATUS_CREATE_COMPLETE:
782 elif status == create_stack.STATUS_CREATE_FAILED: