NSB Prox LW_AFTR Test
[yardstick.git] / tests / unit / orchestrator / test_heat.py
1 #!/usr/bin/env python
2
3 ##############################################################################
4 # Copyright (c) 2017 Intel Corporation
5 #
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 ##############################################################################
11
12 # Unittest for yardstick.benchmark.orchestrator.heat
13 from contextlib import contextmanager
14 from itertools import count
15 from tempfile import NamedTemporaryFile
16 import unittest
17 import uuid
18 import time
19 import mock
20
21 from yardstick.benchmark.contexts import node
22 from yardstick.orchestrator import heat
23
24
25 TARGET_MODULE = 'yardstick.orchestrator.heat'
26
27
28 def mock_patch_target_module(inner_import):
29     return mock.patch('.'.join([TARGET_MODULE, inner_import]))
30
31
32 @contextmanager
33 def timer():
34     start = time.time()
35     data = {'start': start}
36     try:
37         yield data
38     finally:
39         data['end'] = end = time.time()
40         data['delta'] = end - start
41
42
43 def index_value_iter(index, index_value, base_value=None):
44     for current_index in count():
45         if current_index == index:
46             yield index_value
47         else:
48             yield base_value
49
50
51 def get_error_message(error):
52     try:
53         # py2
54         return error.message
55     except AttributeError:
56         # py3
57         return next((arg for arg in error.args if isinstance(arg, str)), None)
58
59
60 class HeatContextTestCase(unittest.TestCase):
61
62     def test_get_short_key_uuid(self):
63         u = uuid.uuid4()
64         k = heat.get_short_key_uuid(u)
65         self.assertEqual(heat.HEAT_KEY_UUID_LENGTH, len(k))
66         self.assertIn(k, str(u))
67
68 class HeatTemplateTestCase(unittest.TestCase):
69
70     def setUp(self):
71         self.template = heat.HeatTemplate('test')
72
73     def test_add_tenant_network(self):
74         self.template.add_network('some-network')
75
76         self.assertEqual(self.template.resources['some-network']['type'], 'OS::Neutron::Net')
77
78     def test_add_provider_network(self):
79         self.template.add_network('some-network', 'physnet2', 'sriov')
80
81         self.assertEqual(self.template.resources['some-network']['type'], 'OS::Neutron::ProviderNet')
82         self.assertEqual(self.template.resources['some-network']['properties']['physical_network'], 'physnet2')
83
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'])
87
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')
90
91     def test_add_router(self):
92         self.template.add_router('some-router', 'ext-net', 'some-subnet')
93
94         self.assertEqual(self.template.resources['some-router']['type'], 'OS::Neutron::Router')
95         self.assertIn('some-subnet', self.template.resources['some-router']['depends_on'])
96
97     def test_add_router_interface(self):
98         self.template.add_router_interface('some-router-if', 'some-router', 'some-subnet')
99
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'])
102
103     def test_add_servergroup(self):
104         self.template.add_servergroup('some-server-group', 'anti-affinity')
105
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'])
108
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()
119
120         heat_stack = heat.HeatStack("tmpStack")
121         self.assertTrue(heat_stack.stacks_exist())
122
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 = {}
129
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",
167                                  image="image1",
168                                  flavor="flavor1",
169                                  flavors=[],
170                                  ports=["port1", "port2"],
171                                  networks=["network1", "network2"],
172                                  scheduler_hints="hints1",
173                                  user="user1",
174                                  key_name="foo-key",
175                                  user_data="user",
176                                  metadata={"cat": 1, "doc": 2},
177                                  additional_properties= {"prop1": 1, "prop2": 2} )
178         heat_template.add_network("network1")
179
180         heat_template.add_flavor("test")
181         self.assertEqual(heat_template.resources['test']['type'], 'OS::Nova::Flavor')
182
183     @mock_patch_target_module('op_utils')
184     @mock_patch_target_module('heatclient')
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
188
189         # populate attributes of the constructed mock
190         mock_heat_client.stacks.get().stack_status_reason = 'the reason'
191
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
196
197         with mock.patch.object(self.template, 'status', return_value=None) as mock_status:
198             # block with timeout hit
199             timeout = 0
200             with self.assertRaises(RuntimeError) as raised, timer() as time_data:
201                 self.template.create(block=True, timeout=timeout)
202
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)
208
209             # ensure the constructor and instance were used
210             self.assertEqual(mock_heat_client_class.call_count, expected_constructor_calls)
211             self.assertEqual(mock_heat_client.stacks.create.call_count, expected_create_calls)
212
213             # ensure that the status was used
214             self.assertGreater(mock_status.call_count, expected_status_calls)
215             expected_status_calls = mock_status.call_count  # synchronize the value
216
217             # ensure the expected exception was raised
218             error_message = get_error_message(raised.exception)
219             self.assertIn('timeout', error_message)
220             self.assertNotIn('the reason', error_message)
221
222             # block with create failed
223             timeout = 10
224             mock_status.side_effect = iter([None, None, u'CREATE_FAILED'])
225             with self.assertRaises(RuntimeError) as raised, timer() as time_data:
226                 self.template.create(block=True, timeout=timeout)
227
228             # ensure the existing heat_client was used and op_utils was used again
229             self.assertEqual(mock_op_utils.get_session.call_count, expected_op_utils_usage)
230             self.assertEqual(mock_op_utils.get_endpoint.call_count, expected_op_utils_usage)
231             self.assertEqual(mock_op_utils.get_heat_api_version.call_count, expected_op_utils_usage)
232
233             # ensure the constructor was not used but the instance was used
234             self.assertEqual(mock_heat_client_class.call_count, expected_constructor_calls)
235             self.assertEqual(mock_heat_client.stacks.create.call_count, expected_create_calls)
236
237             # ensure that the status was used three times
238             expected_status_calls += 3
239             self.assertEqual(mock_status.call_count, expected_status_calls)
240
241     @mock_patch_target_module('op_utils')
242     @mock_patch_target_module('heatclient')
243     def test_create(self, mock_heat_client_class, mock_op_utils):
244         self.template.HEAT_WAIT_LOOP_INTERVAL = 0.2
245         mock_heat_client = mock_heat_client_class()
246
247         # populate attributes of the constructed mock
248         mock_heat_client.stacks.get().outputs = [
249             {'output_key': 'key1', 'output_value': 'value1'},
250             {'output_key': 'key2', 'output_value': 'value2'},
251             {'output_key': 'key3', 'output_value': 'value3'},
252         ]
253         expected_outputs = {
254             'key1': 'value1',
255             'key2': 'value2',
256             'key3': 'value3',
257         }
258
259         expected_status_calls = 0
260         expected_constructor_calls = 1  # above, to get the instance
261         expected_create_calls = 0
262         expected_op_utils_usage = 0
263
264         with mock.patch.object(self.template, 'status') as mock_status:
265             self.template.name = 'no block test'
266             mock_status.return_value = None
267
268             # no block
269             self.assertIsInstance(self.template.create(block=False, timeout=2), heat.HeatStack)
270
271             # ensure op_utils was used
272             expected_op_utils_usage += 1
273             self.assertEqual(mock_op_utils.get_session.call_count, expected_op_utils_usage)
274             self.assertEqual(mock_op_utils.get_endpoint.call_count, expected_op_utils_usage)
275             self.assertEqual(mock_op_utils.get_heat_api_version.call_count, expected_op_utils_usage)
276
277             # ensure the constructor and instance were used
278             self.assertEqual(mock_heat_client_class.call_count, expected_constructor_calls)
279             self.assertEqual(mock_heat_client.stacks.create.call_count, expected_create_calls)
280
281             # ensure that the status was not used
282             self.assertEqual(mock_status.call_count, expected_status_calls)
283
284             # ensure no outputs because this requires blocking
285             self.assertEqual(self.template.outputs, {})
286
287             # block with immediate complete
288             self.template.name = 'block, immediate complete test'
289
290             mock_status.return_value = self.template.HEAT_CREATE_COMPLETE_STATUS
291             self.assertIsInstance(self.template.create(block=True, timeout=2), heat.HeatStack)
292
293             # ensure existing instance was re-used and op_utils was not used
294             self.assertEqual(mock_heat_client_class.call_count, expected_constructor_calls)
295             self.assertEqual(mock_heat_client.stacks.create.call_count, expected_create_calls)
296
297             # ensure status was checked once
298             expected_status_calls += 1
299             self.assertEqual(mock_status.call_count, expected_status_calls)
300
301             # reset template outputs
302             self.template.outputs = None
303
304             # block with delayed complete
305             self.template.name = 'block, delayed complete test'
306
307             success_index = 2
308             mock_status.side_effect = index_value_iter(success_index,
309                                                        self.template.HEAT_CREATE_COMPLETE_STATUS)
310             self.assertIsInstance(self.template.create(block=True, timeout=2), heat.HeatStack)
311
312             # ensure existing instance was re-used and op_utils was not used
313             self.assertEqual(mock_heat_client_class.call_count, expected_constructor_calls)
314             self.assertEqual(mock_heat_client.stacks.create.call_count, expected_create_calls)
315
316             # ensure status was checked three more times
317             expected_status_calls += 1 + success_index
318             self.assertEqual(mock_status.call_count, expected_status_calls)
319
320
321 class HeatStackTestCase(unittest.TestCase):
322
323     def test_delete_calls__delete_multiple_times(self):
324         stack = heat.HeatStack('test')
325         stack.uuid = 1
326         with mock.patch.object(stack, "_delete") as delete_mock:
327             stack.delete()
328         # call once and then call again if uuid is not none
329         self.assertGreater(delete_mock.call_count, 1)
330
331     @mock.patch('yardstick.orchestrator.heat.op_utils')
332     def test_delete_all_calls_delete(self, mock_op):
333         # we must patch the object before we create an instance
334         # so we can override delete() in all the instances
335         with mock.patch.object(heat.HeatStack, "delete") as delete_mock:
336             stack = heat.HeatStack('test')
337             stack.uuid = 1
338             stack.delete_all()
339             self.assertGreater(delete_mock.call_count, 0)