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.
18 from heatclient.exc import HTTPBadRequest
19 from snaps import file_utils
20 from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
21 from snaps.openstack.create_image import OpenStackImage
24 from urllib.request import URLError
26 from urllib2 import URLError
32 from snaps.openstack import create_stack
33 from snaps.openstack.create_stack import (
34 StackSettings, StackSettingsError, StackCreationError)
35 from snaps.openstack.tests import openstack_tests, create_instance_tests
36 from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
37 from snaps.openstack.utils import heat_utils, neutron_utils, nova_utils
39 __author__ = 'spisarski'
41 logger = logging.getLogger('create_stack_tests')
44 class StackSettingsUnitTests(unittest.TestCase):
46 Tests the construction of the StackSettings class
49 def test_no_params(self):
50 with self.assertRaises(StackSettingsError):
53 def test_empty_config(self):
54 with self.assertRaises(StackSettingsError):
55 StackSettings(**dict())
57 def test_name_only(self):
58 with self.assertRaises(StackSettingsError):
59 StackSettings(name='foo')
61 def test_config_with_name_only(self):
62 with self.assertRaises(StackSettingsError):
63 StackSettings(**{'name': 'foo'})
65 def test_config_minimum_template(self):
66 settings = StackSettings(**{'name': 'stack', 'template': 'foo'})
67 self.assertEqual('stack', settings.name)
68 self.assertEqual('foo', settings.template)
69 self.assertIsNone(settings.template_path)
70 self.assertIsNone(settings.env_values)
71 self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
72 settings.stack_create_timeout)
74 def test_config_minimum_template_path(self):
75 settings = StackSettings(**{'name': 'stack', 'template_path': 'foo'})
76 self.assertEqual('stack', settings.name)
77 self.assertIsNone(settings.template)
78 self.assertEqual('foo', settings.template_path)
79 self.assertIsNone(settings.env_values)
80 self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
81 settings.stack_create_timeout)
83 def test_minimum_template(self):
84 settings = StackSettings(name='stack', template='foo')
85 self.assertEqual('stack', settings.name)
86 self.assertEqual('foo', settings.template)
87 self.assertIsNone(settings.template_path)
88 self.assertIsNone(settings.env_values)
89 self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
90 settings.stack_create_timeout)
92 def test_minimum_template_path(self):
93 settings = StackSettings(name='stack', template_path='foo')
94 self.assertEqual('stack', settings.name)
95 self.assertEqual('foo', settings.template_path)
96 self.assertIsNone(settings.template)
97 self.assertIsNone(settings.env_values)
98 self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
99 settings.stack_create_timeout)
102 env_values = {'foo': 'bar'}
103 settings = StackSettings(name='stack', template='bar',
104 template_path='foo', env_values=env_values,
105 stack_create_timeout=999)
106 self.assertEqual('stack', settings.name)
107 self.assertEqual('bar', settings.template)
108 self.assertEqual('foo', settings.template_path)
109 self.assertEqual(env_values, settings.env_values)
110 self.assertEqual(999, settings.stack_create_timeout)
112 def test_config_all(self):
113 env_values = {'foo': 'bar'}
114 settings = StackSettings(
115 **{'name': 'stack', 'template': 'bar', 'template_path': 'foo',
116 'env_values': env_values, 'stack_create_timeout': 999})
117 self.assertEqual('stack', settings.name)
118 self.assertEqual('bar', settings.template)
119 self.assertEqual('foo', settings.template_path)
120 self.assertEqual(env_values, settings.env_values)
121 self.assertEqual(999, settings.stack_create_timeout)
124 class CreateStackSuccessTests(OSIntegrationTestCase):
126 Tests for the CreateStack class defined in create_stack.py
131 Instantiates the CreateStack object that is responsible for downloading
132 and creating an OS stack file within OpenStack
134 super(self.__class__, self).__start__()
136 self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
138 self.heat_creds = self.admin_os_creds
139 self.heat_creds.project_name = self.admin_os_creds.project_name
141 self.heat_cli = heat_utils.heat_client(self.heat_creds)
142 self.stack_creator = None
144 self.image_creator = OpenStackImage(
145 self.heat_creds, openstack_tests.cirros_image_settings(
146 name=self.guid + '-image',
147 image_metadata=self.image_metadata))
148 self.image_creator.create()
151 self.flavor_creator = OpenStackFlavor(
153 FlavorSettings(name=self.guid + '-flavor-name', ram=256, disk=10,
155 self.flavor_creator.create()
157 self.network_name = self.guid + '-net'
158 self.subnet_name = self.guid + '-subnet'
159 self.vm_inst_name = self.guid + '-inst'
162 'image_name': self.image_creator.image_settings.name,
163 'flavor_name': self.flavor_creator.flavor_settings.name,
164 'net_name': self.network_name,
165 'subnet_name': self.subnet_name,
166 'inst_name': self.vm_inst_name}
168 self.heat_tmplt_path = pkg_resources.resource_filename(
169 'snaps.openstack.tests.heat', 'test_heat_template.yaml')
173 Cleans the stack and downloaded stack file
175 if self.stack_creator:
177 self.stack_creator.clean()
181 if self.image_creator:
183 self.image_creator.clean()
187 if self.flavor_creator:
189 self.flavor_creator.clean()
193 super(self.__class__, self).__clean__()
195 def test_create_stack_template_file(self):
197 Tests the creation of an OpenStack stack from Heat template file.
200 # Set the default stack settings, then set any custom parameters sent
202 stack_settings = StackSettings(
203 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
204 template_path=self.heat_tmplt_path,
205 env_values=self.env_values)
206 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
208 created_stack = self.stack_creator.create()
209 self.assertIsNotNone(created_stack)
211 retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
213 self.assertIsNotNone(retrieved_stack)
214 self.assertEqual(created_stack.name, retrieved_stack.name)
215 self.assertEqual(created_stack.id, retrieved_stack.id)
216 self.assertEqual(0, len(self.stack_creator.get_outputs()))
218 def test_create_stack_short_timeout(self):
220 Tests the creation of an OpenStack stack from Heat template file.
223 # Set the default stack settings, then set any custom parameters sent
225 stack_settings = StackSettings(
226 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
227 template_path=self.heat_tmplt_path,
228 env_values=self.env_values, stack_create_timeout=0)
230 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
232 with self.assertRaises(StackCreationError):
233 self.stack_creator.create()
235 def test_create_stack_template_dict(self):
237 Tests the creation of an OpenStack stack from a heat dict() object.
240 # Set the default stack settings, then set any custom parameters sent
242 template_dict = heat_utils.parse_heat_template_str(
243 file_utils.read_file(self.heat_tmplt_path))
244 stack_settings = StackSettings(
245 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
246 template=template_dict,
247 env_values=self.env_values)
248 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
250 created_stack = self.stack_creator.create()
251 self.assertIsNotNone(created_stack)
253 retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
255 self.assertIsNotNone(retrieved_stack)
256 self.assertEqual(created_stack.name, retrieved_stack.name)
257 self.assertEqual(created_stack.id, retrieved_stack.id)
258 self.assertEqual(0, len(self.stack_creator.get_outputs()))
260 def test_create_delete_stack(self):
262 Tests the creation then deletion of an OpenStack stack to ensure
263 clean() does not raise an Exception.
266 template_dict = heat_utils.parse_heat_template_str(
267 file_utils.read_file(self.heat_tmplt_path))
268 stack_settings = StackSettings(
269 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
270 template=template_dict,
271 env_values=self.env_values)
272 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
274 created_stack = self.stack_creator.create()
275 self.assertIsNotNone(created_stack)
277 retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
279 self.assertIsNotNone(retrieved_stack)
280 self.assertEqual(created_stack.name, retrieved_stack.name)
281 self.assertEqual(created_stack.id, retrieved_stack.id)
282 self.assertEqual(0, len(self.stack_creator.get_outputs()))
283 self.assertEqual(create_stack.STATUS_CREATE_COMPLETE,
284 self.stack_creator.get_status())
286 # Delete Stack manually
287 heat_utils.delete_stack(self.heat_cli, created_stack)
289 end_time = time.time() + 90
291 while time.time() < end_time:
292 status = heat_utils.get_stack_status(self.heat_cli,
294 if status == create_stack.STATUS_DELETE_COMPLETE:
298 self.assertTrue(deleted)
300 # Must not throw an exception when attempting to cleanup non-existent
302 self.stack_creator.clean()
303 self.assertIsNone(self.stack_creator.get_stack())
305 def test_create_same_stack(self):
307 Tests the creation of an OpenStack stack when the stack already exists.
310 template_dict = heat_utils.parse_heat_template_str(
311 file_utils.read_file(self.heat_tmplt_path))
312 stack_settings = StackSettings(
313 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
314 template=template_dict,
315 env_values=self.env_values)
316 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
318 created_stack1 = self.stack_creator.create()
320 retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
322 self.assertIsNotNone(retrieved_stack)
323 self.assertEqual(created_stack1.name, retrieved_stack.name)
324 self.assertEqual(created_stack1.id, retrieved_stack.id)
325 self.assertEqual(0, len(self.stack_creator.get_outputs()))
327 # Should be retrieving the instance data
328 stack_creator2 = create_stack.OpenStackHeatStack(self.heat_creds,
330 stack2 = stack_creator2.create()
331 self.assertEqual(created_stack1.id, stack2.id)
333 def test_retrieve_network_creators(self):
335 Tests the creation of an OpenStack stack from Heat template file and
336 the retrieval of the network creator.
338 stack_settings = StackSettings(
339 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
340 template_path=self.heat_tmplt_path,
341 env_values=self.env_values)
342 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
344 created_stack = self.stack_creator.create()
345 self.assertIsNotNone(created_stack)
347 net_creators = self.stack_creator.get_network_creators()
348 self.assertIsNotNone(net_creators)
349 self.assertEqual(1, len(net_creators))
350 self.assertEqual(self.network_name, net_creators[0].get_network().name)
352 neutron = neutron_utils.neutron_client(self.os_creds)
353 net_by_name = neutron_utils.get_network(
354 neutron, network_name=net_creators[0].get_network().name)
355 self.assertEqual(net_creators[0].get_network(), net_by_name)
356 self.assertIsNotNone(neutron_utils.get_network_by_id(
357 neutron, net_creators[0].get_network().id))
359 self.assertEqual(1, len(net_creators[0].get_subnets()))
360 subnet = net_creators[0].get_subnets()[0]
361 subnet_by_name = neutron_utils.get_subnet(
362 neutron, subnet_name=subnet.name)
363 self.assertEqual(subnet, subnet_by_name)
365 subnet_by_id = neutron_utils.get_subnet_by_id(neutron, subnet.id)
366 self.assertIsNotNone(subnet_by_id)
367 self.assertEqual(subnet_by_name, subnet_by_id)
369 def test_retrieve_vm_inst_creators(self):
371 Tests the creation of an OpenStack stack from Heat template file and
372 the retrieval of the network creator.
374 stack_settings = StackSettings(
375 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
376 template_path=self.heat_tmplt_path,
377 env_values=self.env_values)
378 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
380 created_stack = self.stack_creator.create()
381 self.assertIsNotNone(created_stack)
383 vm_inst_creators = self.stack_creator.get_vm_inst_creators()
384 self.assertIsNotNone(vm_inst_creators)
385 self.assertEqual(1, len(vm_inst_creators))
386 self.assertEqual(self.vm_inst_name,
387 vm_inst_creators[0].get_vm_inst().name)
389 nova = nova_utils.nova_client(self.admin_os_creds)
390 vm_inst_by_name = nova_utils.get_server(
391 nova, server_name=vm_inst_creators[0].get_vm_inst().name)
392 self.assertEqual(vm_inst_creators[0].get_vm_inst(), vm_inst_by_name)
393 self.assertIsNotNone(nova_utils.get_server_object_by_id(
394 nova, vm_inst_creators[0].get_vm_inst().id))
397 class CreateComplexStackTests(OSIntegrationTestCase):
399 Tests for the CreateStack class defined in create_stack.py
404 Instantiates the CreateStack object that is responsible for downloading
405 and creating an OS stack file within OpenStack
407 super(self.__class__, self).__start__()
409 self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
411 self.heat_creds = self.admin_os_creds
412 self.heat_creds.project_name = self.admin_os_creds.project_name
414 self.heat_cli = heat_utils.heat_client(self.heat_creds)
415 self.stack_creator = None
417 self.image_creator = OpenStackImage(
418 self.heat_creds, openstack_tests.cirros_image_settings(
419 name=self.guid + '-image',
420 image_metadata=self.image_metadata))
421 self.image_creator.create()
423 self.network_name = self.guid + '-net'
424 self.subnet_name = self.guid + '-subnet'
425 self.flavor1_name = self.guid + '-flavor1'
426 self.flavor2_name = self.guid + '-flavor2'
427 self.vm_inst1_name = self.guid + '-inst1'
428 self.vm_inst2_name = self.guid + '-inst2'
429 self.keypair_name = self.guid + '-kp'
432 'image1_name': self.image_creator.image_settings.name,
433 'image2_name': self.image_creator.image_settings.name,
434 'flavor1_name': self.flavor1_name,
435 'flavor2_name': self.flavor2_name,
436 'net_name': self.network_name,
437 'subnet_name': self.subnet_name,
438 'inst1_name': self.vm_inst1_name,
439 'inst2_name': self.vm_inst2_name,
440 'keypair_name': self.keypair_name}
442 self.heat_tmplt_path = pkg_resources.resource_filename(
443 'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
447 Cleans the stack and downloaded stack file
449 if self.stack_creator:
451 self.stack_creator.clean()
455 if self.image_creator:
457 self.image_creator.clean()
461 super(self.__class__, self).__clean__()
463 def test_connect_via_ssh_heat_vm(self):
465 Tests the creation of an OpenStack stack from Heat template file and
466 the retrieval of two VM instance creators and attempt to connect via
467 SSH to the first one with a floating IP.
469 stack_settings = StackSettings(
470 name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
471 template_path=self.heat_tmplt_path,
472 env_values=self.env_values)
473 self.stack_creator = create_stack.OpenStackHeatStack(
474 self.heat_creds, stack_settings,
475 [self.image_creator.image_settings])
476 created_stack = self.stack_creator.create()
477 self.assertIsNotNone(created_stack)
479 vm_inst_creators = self.stack_creator.get_vm_inst_creators(
480 heat_keypair_option='private_key')
481 self.assertIsNotNone(vm_inst_creators)
482 self.assertEqual(2, len(vm_inst_creators))
484 for vm_inst_creator in vm_inst_creators:
485 if vm_inst_creator.get_vm_inst().name == self.vm_inst1_name:
487 create_instance_tests.validate_ssh_client(vm_inst_creator))
489 vm_settings = vm_inst_creator.instance_settings
490 self.assertEqual(0, len(vm_settings.floating_ip_settings))
493 class CreateStackNegativeTests(OSIntegrationTestCase):
495 Negative test cases for the CreateStack class
499 super(self.__class__, self).__start__()
501 self.heat_creds = self.admin_os_creds
502 self.heat_creds.project_name = self.admin_os_creds.project_name
504 self.stack_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
505 self.stack_creator = None
506 self.heat_tmplt_path = pkg_resources.resource_filename(
507 'snaps.openstack.tests.heat', 'test_heat_template.yaml')
510 if self.stack_creator:
511 self.stack_creator.clean()
512 super(self.__class__, self).__clean__()
514 def test_missing_dependencies(self):
516 Expect an StackCreationError when the stack file does not exist
518 stack_settings = StackSettings(name=self.stack_name,
519 template_path=self.heat_tmplt_path)
520 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
522 with self.assertRaises(HTTPBadRequest):
523 self.stack_creator.create()
525 def test_bad_stack_file(self):
527 Expect an StackCreationError when the stack file does not exist
529 stack_settings = StackSettings(name=self.stack_name,
531 self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
533 with self.assertRaises(IOError):
534 self.stack_creator.create()