3 ##############################################################################
4 # Copyright (c) 2017 Intel Corporation
6 # All rights reserved. This program and the accompanying materials
7 # are made available under the terms of the Apache License, Version 2.0
8 # which accompanies this distribution, and is available at
9 # http://www.apache.org/licenses/LICENSE-2.0
10 ##############################################################################
12 # Unittest for yardstick.benchmark.orchestrator.heat
13 from contextlib import contextmanager
14 from itertools import count
15 from tempfile import NamedTemporaryFile
21 from yardstick.benchmark.contexts import node
22 from yardstick.orchestrator import heat
25 TARGET_MODULE = 'yardstick.orchestrator.heat'
28 def mock_patch_target_module(inner_import):
29 return mock.patch('.'.join([TARGET_MODULE, inner_import]))
35 data = {'start': start}
39 data['end'] = end = time.time()
40 data['delta'] = end - start
43 def index_value_iter(index, index_value, base_value=None):
44 for current_index in count():
45 if current_index == index:
51 def get_error_message(error):
55 except AttributeError:
57 return next((arg for arg in error.args if isinstance(arg, str)), None)
60 class HeatContextTestCase(unittest.TestCase):
62 def test_get_short_key_uuid(self):
64 k = heat.get_short_key_uuid(u)
65 self.assertEqual(heat.HEAT_KEY_UUID_LENGTH, len(k))
66 self.assertIn(k, str(u))
68 class HeatTemplateTestCase(unittest.TestCase):
71 self.template = heat.HeatTemplate('test')
73 def test_add_tenant_network(self):
74 self.template.add_network('some-network')
76 self.assertEqual(self.template.resources['some-network']['type'], 'OS::Neutron::Net')
78 def test_add_provider_network(self):
79 self.template.add_network('some-network', 'physnet2', 'sriov')
81 self.assertEqual(self.template.resources['some-network']['type'], 'OS::Neutron::ProviderNet')
82 self.assertEqual(self.template.resources['some-network']['properties']['physical_network'], 'physnet2')
84 def test_add_subnet(self):
85 netattrs = {'cidr': '10.0.0.0/24', 'provider': None, 'external_network': 'ext_net'}
86 self.template.add_subnet('some-subnet', "some-network", netattrs['cidr'])
88 self.assertEqual(self.template.resources['some-subnet']['type'], 'OS::Neutron::Subnet')
89 self.assertEqual(self.template.resources['some-subnet']['properties']['cidr'], '10.0.0.0/24')
91 def test_add_router(self):
92 self.template.add_router('some-router', 'ext-net', 'some-subnet')
94 self.assertEqual(self.template.resources['some-router']['type'], 'OS::Neutron::Router')
95 self.assertIn('some-subnet', self.template.resources['some-router']['depends_on'])
97 def test_add_router_interface(self):
98 self.template.add_router_interface('some-router-if', 'some-router', 'some-subnet')
100 self.assertEqual(self.template.resources['some-router-if']['type'], 'OS::Neutron::RouterInterface')
101 self.assertIn('some-subnet', self.template.resources['some-router-if']['depends_on'])
103 def test_add_servergroup(self):
104 self.template.add_servergroup('some-server-group', 'anti-affinity')
106 self.assertEqual(self.template.resources['some-server-group']['type'], 'OS::Nova::ServerGroup')
107 self.assertEqual(self.template.resources['some-server-group']['properties']['policies'], ['anti-affinity'])
109 def test__add_resources_to_template_raw(self):
110 test_context = node.NodeContext()
111 test_context.name = 'foo'
112 test_context.template_file = '/tmp/some-heat-file'
113 test_context.heat_parameters = {'image': 'cirros'}
114 test_context.key_filename = "/tmp/1234"
115 test_context.keypair_name = "foo-key"
116 test_context.secgroup_name = "foo-secgroup"
117 test_context.key_uuid = "2f2e4997-0a8e-4eb7-9fa4-f3f8fbbc393b"
118 heat_object = heat.HeatObject()
120 heat_stack = heat.HeatStack("tmpStack")
121 self.assertTrue(heat_stack.stacks_exist())
123 test_context.tmpfile = NamedTemporaryFile(delete=True, mode='w+t')
124 test_context.tmpfile.write("heat_template_version: 2015-04-30")
125 test_context.tmpfile.flush()
126 test_context.tmpfile.seek(0)
127 heat_template = heat.HeatTemplate(heat_object)
128 heat_template.resources = {}
130 heat_template.add_network("network1")
131 heat_template.add_network("network2")
132 heat_template.add_security_group("sec_group1")
133 heat_template.add_security_group("sec_group2")
134 heat_template.add_subnet("subnet1", "network1", "cidr1")
135 heat_template.add_subnet("subnet2", "network2", "cidr2")
136 heat_template.add_router("router1", "gw1", "subnet1")
137 heat_template.add_router_interface("router_if1", "router1", "subnet1")
138 heat_template.add_port("port1", "network1", "subnet1", "normal")
139 heat_template.add_port("port2", "network2", "subnet2", "normal", sec_group_id="sec_group1",provider="not-sriov")
140 heat_template.add_port("port3", "network2", "subnet2", "normal", sec_group_id="sec_group1",provider="sriov")
141 heat_template.add_floating_ip("floating_ip1", "network1", "port1", "router_if1")
142 heat_template.add_floating_ip("floating_ip2", "network2", "port2", "router_if2", "foo-secgroup")
143 heat_template.add_floating_ip_association("floating_ip1_association", "floating_ip1", "port1")
144 heat_template.add_servergroup("server_grp2", "affinity")
145 heat_template.add_servergroup("server_grp3", "anti-affinity")
146 heat_template.add_security_group("security_group")
147 heat_template.add_server(name="server1", image="image1", flavor="flavor1", flavors=[])
148 heat_template.add_server_group(name="servergroup", policies=["policy1","policy2"])
149 heat_template.add_server_group(name="servergroup", policies="policy1")
150 heat_template.add_server(name="server2", image="image1", flavor="flavor1", flavors=[], ports=["port1", "port2"],
151 networks=["network1", "network2"], scheduler_hints="hints1", user="user1",
152 key_name="foo-key", user_data="user", metadata={"cat": 1, "doc": 2},
153 additional_properties={"prop1": 1, "prop2": 2})
154 heat_template.add_server(name="server2", image="image1", flavor="flavor1", flavors=["flavor1", "flavor2"],
155 ports=["port1", "port2"],
156 networks=["network1", "network2"], scheduler_hints="hints1", user="user1",
157 key_name="foo-key", user_data="user", metadata={"cat": 1, "doc": 2},
158 additional_properties={"prop1": 1, "prop2": 2} )
159 heat_template.add_server(name="server2", image="image1", flavor="flavor1", flavors=["flavor3", "flavor4"],
160 ports=["port1", "port2"],
161 networks=["network1", "network2"], scheduler_hints="hints1", user="user1",
162 key_name="foo-key", user_data="user", metadata={"cat": 1, "doc": 2},
163 additional_properties={"prop1": 1, "prop2": 2})
164 heat_template.add_flavor(name="flavor1", vcpus=1, ram=2048, disk=1,extra_specs={"cat": 1, "dog": 2})
165 heat_template.add_flavor(name=None, vcpus=1, ram=2048)
166 heat_template.add_server(name="server1",
170 ports=["port1", "port2"],
171 networks=["network1", "network2"],
172 scheduler_hints="hints1",
176 metadata={"cat": 1, "doc": 2},
177 additional_properties= {"prop1": 1, "prop2": 2} )
178 heat_template.add_network("network1")
180 heat_template.add_flavor("test")
181 self.assertEqual(heat_template.resources['test']['type'], 'OS::Nova::Flavor')
183 @mock_patch_target_module('op_utils')
184 @mock_patch_target_module('heatclient.client.Client')
185 def test_create_negative(self, mock_heat_client_class, mock_op_utils):
186 self.template.HEAT_WAIT_LOOP_INTERVAL = 0
187 mock_heat_client = mock_heat_client_class() # get the constructed mock
189 # populate attributes of the constructed mock
190 mock_heat_client.stacks.get().stack_status_reason = 'the reason'
192 expected_status_calls = 0
193 expected_constructor_calls = 1 # above, to get the instance
194 expected_create_calls = 0
195 expected_op_utils_usage = 0
197 with mock.patch.object(self.template, 'status', return_value=None) as mock_status:
198 # block with timeout hit
200 with self.assertRaises(RuntimeError) as raised, timer() as time_data:
201 self.template.create(block=True, timeout=timeout)
203 # ensure op_utils was used
204 expected_op_utils_usage += 1
205 self.assertEqual(mock_op_utils.get_session.call_count, expected_op_utils_usage)
206 self.assertEqual(mock_op_utils.get_endpoint.call_count, expected_op_utils_usage)
207 self.assertEqual(mock_op_utils.get_heat_api_version.call_count, expected_op_utils_usage)
209 # ensure the constructor and instance were used
210 expected_constructor_calls += 1
211 expected_create_calls += 1
212 self.assertEqual(mock_heat_client_class.call_count, expected_constructor_calls)
213 self.assertEqual(mock_heat_client.stacks.create.call_count, expected_create_calls)
215 # ensure that the status was used
216 self.assertGreater(mock_status.call_count, expected_status_calls)
217 expected_status_calls = mock_status.call_count # synchronize the value
219 # ensure the expected exception was raised
220 error_message = get_error_message(raised.exception)
221 self.assertIn('timeout', error_message)
222 self.assertNotIn('the reason', error_message)
224 # block with create failed
226 mock_status.side_effect = iter([None, None, u'CREATE_FAILED'])
227 with self.assertRaises(RuntimeError) as raised, timer() as time_data:
228 self.template.create(block=True, timeout=timeout)
230 # ensure the existing heat_client was used and op_utils was used again
231 self.assertEqual(mock_op_utils.get_session.call_count, expected_op_utils_usage)
232 self.assertEqual(mock_op_utils.get_endpoint.call_count, expected_op_utils_usage)
233 self.assertEqual(mock_op_utils.get_heat_api_version.call_count, expected_op_utils_usage)
235 # ensure the constructor was not used but the instance was used
236 expected_create_calls += 1
237 self.assertEqual(mock_heat_client_class.call_count, expected_constructor_calls)
238 self.assertEqual(mock_heat_client.stacks.create.call_count, expected_create_calls)
240 # ensure that the status was used three times
241 expected_status_calls += 3
242 self.assertEqual(mock_status.call_count, expected_status_calls)
244 # ensure the expected exception was raised
245 error_message = get_error_message(raised.exception)
246 self.assertNotIn('timeout', error_message)
247 self.assertIn('the reason', error_message)
249 @mock_patch_target_module('op_utils')
250 @mock_patch_target_module('heatclient.client.Client')
251 def test_create(self, mock_heat_client_class, mock_op_utils):
252 self.template.HEAT_WAIT_LOOP_INTERVAL = 0.2
253 mock_heat_client = mock_heat_client_class()
255 # populate attributes of the constructed mock
256 mock_heat_client.stacks.get().outputs = [
257 {'output_key': 'key1', 'output_value': 'value1'},
258 {'output_key': 'key2', 'output_value': 'value2'},
259 {'output_key': 'key3', 'output_value': 'value3'},
267 expected_status_calls = 0
268 expected_constructor_calls = 1 # above, to get the instance
269 expected_create_calls = 0
270 expected_op_utils_usage = 0
272 with mock.patch.object(self.template, 'status') as mock_status:
273 self.template.name = 'no block test'
274 mock_status.return_value = None
277 self.assertIsInstance(self.template.create(block=False, timeout=2), heat.HeatStack)
279 # ensure op_utils was used
280 expected_op_utils_usage += 1
281 self.assertEqual(mock_op_utils.get_session.call_count, expected_op_utils_usage)
282 self.assertEqual(mock_op_utils.get_endpoint.call_count, expected_op_utils_usage)
283 self.assertEqual(mock_op_utils.get_heat_api_version.call_count, expected_op_utils_usage)
285 # ensure the constructor and instance were used
286 expected_constructor_calls += 1
287 expected_create_calls += 1
288 self.assertEqual(mock_heat_client_class.call_count, expected_constructor_calls)
289 self.assertEqual(mock_heat_client.stacks.create.call_count, expected_create_calls)
291 # ensure that the status was not used
292 self.assertEqual(mock_status.call_count, expected_status_calls)
294 # ensure no outputs because this requires blocking
295 self.assertEqual(self.template.outputs, {})
297 # block with immediate complete
298 self.template.name = 'block, immediate complete test'
300 mock_status.return_value = self.template.HEAT_CREATE_COMPLETE_STATUS
301 self.assertIsInstance(self.template.create(block=True, timeout=2), heat.HeatStack)
303 # ensure existing instance was re-used and op_utils was not used
304 expected_create_calls += 1
305 self.assertEqual(mock_heat_client_class.call_count, expected_constructor_calls)
306 self.assertEqual(mock_heat_client.stacks.create.call_count, expected_create_calls)
308 # ensure status was checked once
309 expected_status_calls += 1
310 self.assertEqual(mock_status.call_count, expected_status_calls)
312 # ensure the expected outputs are present
313 self.assertDictEqual(self.template.outputs, expected_outputs)
315 # reset template outputs
316 self.template.outputs = None
318 # block with delayed complete
319 self.template.name = 'block, delayed complete test'
322 mock_status.side_effect = index_value_iter(success_index,
323 self.template.HEAT_CREATE_COMPLETE_STATUS)
324 self.assertIsInstance(self.template.create(block=True, timeout=2), heat.HeatStack)
326 # ensure existing instance was re-used and op_utils was not used
327 expected_create_calls += 1
328 self.assertEqual(mock_heat_client_class.call_count, expected_constructor_calls)
329 self.assertEqual(mock_heat_client.stacks.create.call_count, expected_create_calls)
331 # ensure status was checked three more times
332 expected_status_calls += 1 + success_index
333 self.assertEqual(mock_status.call_count, expected_status_calls)
336 class HeatStackTestCase(unittest.TestCase):
338 def test_delete_calls__delete_multiple_times(self):
339 stack = heat.HeatStack('test')
341 with mock.patch.object(stack, "_delete") as delete_mock:
343 # call once and then call again if uuid is not none
344 self.assertGreater(delete_mock.call_count, 1)
346 @mock.patch('yardstick.orchestrator.heat.op_utils')
347 def test_delete_all_calls_delete(self, mock_op):
348 # we must patch the object before we create an instance
349 # so we can override delete() in all the instances
350 with mock.patch.object(heat.HeatStack, "delete") as delete_mock:
351 stack = heat.HeatStack('test')
354 self.assertGreater(delete_mock.call_count, 0)