Add "restartPolicy" parameter in Kubernetes policy
[yardstick.git] / yardstick / tests / unit / orchestrator / test_heat.py
1 ##############################################################################
2 # Copyright (c) 2017 Intel Corporation
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 import tempfile
11
12 import munch
13 import mock
14 from oslo_serialization import jsonutils
15 from oslo_utils import uuidutils
16 import shade
17 import unittest
18
19 from yardstick.benchmark.contexts import node
20 from yardstick.common import constants
21 from yardstick.common import exceptions
22 from yardstick.orchestrator import heat
23
24
25 class FakeStack(object):
26
27     def __init__(self, outputs=None, status=None, id=None):
28         self.outputs = outputs
29         self.status = status
30         self.id = id
31
32
33 class HeatStackTestCase(unittest.TestCase):
34
35     def setUp(self):
36         self.stack_name = 'STACK NAME'
37         with mock.patch.object(shade, 'openstack_cloud'):
38             self.heatstack = heat.HeatStack(self.stack_name)
39         self._mock_stack_create = mock.patch.object(self.heatstack._cloud,
40                                                     'create_stack')
41         self.mock_stack_create = self._mock_stack_create.start()
42         self._mock_stack_delete = mock.patch.object(self.heatstack._cloud,
43                                                     'delete_stack')
44         self.mock_stack_delete = self._mock_stack_delete.start()
45         self._mock_stack_get = mock.patch.object(self.heatstack._cloud,
46                                                  'get_stack')
47         self.mock_stack_get = self._mock_stack_get.start()
48
49         self.addCleanup(self._cleanup)
50
51     def _cleanup(self):
52         self._mock_stack_create.stop()
53         self._mock_stack_delete.stop()
54         self._mock_stack_get.stop()
55         heat._DEPLOYED_STACKS = {}
56
57     @mock.patch.object(shade, 'openstack_cloud')
58     def test__init(self, mock_openstack_cloud):
59         os_cloud_config = {'key': 'value'}
60         heatstack = heat.HeatStack('name', os_cloud_config=os_cloud_config)
61         self.assertEqual('name', heatstack.name)
62         os_cloud_config.update(constants.OS_CLOUD_DEFAULT_CONFIG)
63         mock_openstack_cloud.assert_called_once_with(**os_cloud_config)
64
65     def test_create(self):
66         template = {'tkey': 'tval'}
67         heat_parameters = {'pkey': 'pval'}
68         outputs = [{'output_key': 'okey', 'output_value': 'oval'}]
69         id = uuidutils.generate_uuid()
70         self.mock_stack_create.return_value = FakeStack(
71             outputs=outputs, status=mock.Mock(), id=id)
72         mock_tfile = mock.Mock()
73         with mock.patch.object(tempfile._TemporaryFileWrapper, '__enter__',
74                                return_value=mock_tfile):
75             self.heatstack.create(template, heat_parameters, True, 100)
76             mock_tfile.write.assert_called_once_with(jsonutils.dump_as_bytes(template))
77             mock_tfile.close.assert_called_once()
78
79         self.mock_stack_create.assert_called_once_with(
80             self.stack_name, template_file=mock_tfile.name, wait=True,
81             timeout=100, pkey='pval')
82         self.assertEqual({'okey': 'oval'}, self.heatstack.outputs)
83         self.assertEqual(heat._DEPLOYED_STACKS[id], self.heatstack._stack)
84
85     def test_stacks_exist(self):
86         self.assertEqual(0, self.heatstack.stacks_exist())
87         heat._DEPLOYED_STACKS['id'] = 'stack'
88         self.assertEqual(1, self.heatstack.stacks_exist())
89
90     def test_delete_not_uuid(self):
91         self.assertIsNone(self.heatstack.delete())
92
93     def test_delete_existing_uuid(self):
94         id = uuidutils.generate_uuid()
95         self.heatstack._stack = FakeStack(
96             outputs=mock.Mock(), status=mock.Mock(), id=id)
97         heat._DEPLOYED_STACKS[id] = self.heatstack._stack
98         delete_return = mock.Mock()
99         self.mock_stack_delete.return_value = delete_return
100
101         ret = self.heatstack.delete(wait=True)
102         self.assertEqual(delete_return, ret)
103         self.assertFalse(heat._DEPLOYED_STACKS)
104         self.mock_stack_delete.assert_called_once_with(id, wait=True)
105
106     def test_delete_bug_in_shade(self):
107         id = uuidutils.generate_uuid()
108         self.heatstack._stack = FakeStack(
109             outputs=mock.Mock(), status=mock.Mock(), id=id)
110         heat._DEPLOYED_STACKS[id] = self.heatstack._stack
111         self.mock_stack_delete.side_effect = TypeError()
112
113         ret = self.heatstack.delete(wait=True)
114         self.assertTrue(ret)
115         self.assertFalse(heat._DEPLOYED_STACKS)
116         self.mock_stack_delete.assert_called_once_with(id, wait=True)
117
118     def test_get(self):
119         # make sure shade/get_stack is called with the appropriate vars
120         self.mock_stack_get.return_value = munch.Munch(
121             id="my-existing-stack-id",
122             outputs=[
123                 {
124                  u'output_value': u'b734d06a-dec7-...',
125                  u'output_key': u'ares.demo-test-port-network_id',
126                  u'description': u''
127                 },
128                 {u'output_value': u'b08da78c-2218-...',
129                  u'output_key': u'ares.demo-test-port-subnet_id',
130                  u'description': u''
131                 },
132                 {u'output_value': u'10.0.1.0/24',
133                  u'output_key': u'demo-test-subnet-cidr',
134                  u'description': u''
135                 },
136                 {u'output_value': u'b08da78c-2218-...',
137                  u'output_key': u'demo-test-subnet',
138                  u'description': u''
139                 },
140                 {u'output_value': u'b1a03624-aefc-...',
141                  u'output_key': u'ares.demo',
142                  u'description': u''
143                 },
144                 {u'output_value': u'266a8088-c630-...',
145                  u'output_key': u'demo-secgroup',
146                  u'description': u''
147                 },
148                 {u'output_value': u'10.0.1.5',
149                  u'output_key': u'ares.demo-test-port',
150                  u'description': u''
151                 },
152                 {u'output_value': u'10.0.1.1',
153                  u'output_key': u'demo-test-subnet-gateway_ip',
154                  u'description': u''
155                 },
156                 {u'output_value': u'',
157                  u'output_key': u'ares.demo-test-port-device_id',
158                  u'description': u''
159                 },
160                 {u'output_value': u'172.24.4.7',
161                  u'output_key': u'ares.demo-fip',
162                  u'description': u''
163                 },
164                 {u'output_value': u'fa:16:3e:6c:c3:0f',
165                  u'output_key': u'ares.demo-test-port-mac_address',
166                  u'description': u''}
167             ]
168         )
169         expected_outputs = {
170             'ares.demo-test-port-network_id': 'b734d06a-dec7-...',
171             'ares.demo-test-port-subnet_id': 'b08da78c-2218-...',
172             'demo-test-subnet-cidr': '10.0.1.0/24',
173             'demo-test-subnet': 'b08da78c-2218-...',
174             'ares.demo': 'b1a03624-aefc-...',
175             'demo-secgroup': '266a8088-c630-...',
176             'ares.demo-test-port': '10.0.1.5',
177             'demo-test-subnet-gateway_ip': '10.0.1.1',
178             'ares.demo-test-port-device_id': '',
179             'ares.demo-fip': '172.24.4.7',
180             'ares.demo-test-port-mac_address': 'fa:16:3e:6c:c3:0f',
181         }
182
183         stack_id = "my-existing-stack-id"
184         self.heatstack.name = "my-existing-stack"
185         self.heatstack.get()
186
187         self.mock_stack_get.assert_called_once_with(self.heatstack.name)
188         self.assertEqual(expected_outputs, self.heatstack.outputs)
189         self.assertEqual(1, len(heat._DEPLOYED_STACKS))
190         self.assertEqual(self.heatstack._stack,
191                          heat._DEPLOYED_STACKS[stack_id])
192
193     def test_get_invalid_name(self):
194         # No context matching this name exists
195         self.mock_stack_get.return_value = []
196         self.heatstack.name = 'not-a-stack'
197         self.heatstack.get()
198         self.assertEqual(0, len(heat._DEPLOYED_STACKS))
199
200
201 class HeatTemplateTestCase(unittest.TestCase):
202
203     def setUp(self):
204         self._os_cloud_config = {'key1': 'value1'}
205         self.template = heat.HeatTemplate(
206             'test', os_cloud_config=self._os_cloud_config)
207
208     def test_add_tenant_network(self):
209         self.template.add_network('some-network')
210
211         self.assertEqual('OS::Neutron::Net',
212                          self.template.resources['some-network']['type'])
213
214     def test_add_provider_network(self):
215         self.template.add_network('some-network', 'physnet2', 'sriov')
216
217         self.assertEqual(self.template.resources['some-network']['type'],
218                          'OS::Neutron::ProviderNet')
219         self.assertEqual(self.template.resources['some-network'][
220                              'properties']['physical_network'], 'physnet2')
221
222     def test_add_subnet(self):
223         netattrs = {'cidr': '10.0.0.0/24',
224                     'provider': None,
225                     'external_network': 'ext_net'}
226         self.template.add_subnet('some-subnet', "some-network",
227                                  netattrs['cidr'])
228
229         self.assertEqual(self.template.resources['some-subnet']['type'],
230                          'OS::Neutron::Subnet')
231         self.assertEqual(self.template.resources['some-subnet']['properties'][
232                              'cidr'], '10.0.0.0/24')
233
234     def test_add_router(self):
235         self.template.add_router('some-router', 'ext-net', 'some-subnet')
236
237         self.assertEqual(self.template.resources['some-router']['type'],
238                          'OS::Neutron::Router')
239         self.assertIn('some-subnet',
240                       self.template.resources['some-router']['depends_on'])
241
242     def test_add_router_interface(self):
243         self.template.add_router_interface('some-router-if', 'some-router',
244                                            'some-subnet')
245
246         self.assertEqual(self.template.resources['some-router-if']['type'],
247                          'OS::Neutron::RouterInterface')
248         self.assertIn('some-subnet',
249                       self.template.resources['some-router-if']['depends_on'])
250
251     def test_add_servergroup(self):
252         self.template.add_servergroup('some-server-group', 'anti-affinity')
253
254         self.assertEqual(self.template.resources['some-server-group']['type'],
255                          'OS::Nova::ServerGroup')
256         self.assertEqual(self.template.resources['some-server-group'][
257                              'properties']['policies'], ['anti-affinity'])
258
259     def test__add_resources_to_template_raw(self):
260         test_context = node.NodeContext()
261         self.addCleanup(test_context._delete_context)
262         test_context._name = 'foo'
263         test_context.template_file = '/tmp/some-heat-file'
264         test_context.heat_parameters = {'image': 'cirros'}
265         test_context.key_filename = "/tmp/1234"
266         test_context.keypair_name = "foo-key"
267         test_context.secgroup_name = "foo-secgroup"
268         test_context.key_uuid = "2f2e4997-0a8e-4eb7-9fa4-f3f8fbbc393b"
269
270         test_context.tmpfile = tempfile.NamedTemporaryFile(
271             delete=True, mode='w+t')
272         test_context.tmpfile.write("heat_template_version: 2015-04-30")
273         test_context.tmpfile.flush()
274         test_context.tmpfile.seek(0)
275         heat_template = heat.HeatTemplate('template name')
276         heat_template.resources = {}
277
278         heat_template.add_network("network1")
279         heat_template.add_network("network2")
280         heat_template.add_security_group("sec_group1")
281         heat_template.add_security_group("sec_group2")
282         heat_template.add_subnet("subnet1", "network1", "cidr1")
283         heat_template.add_subnet("subnet2", "network2", "cidr2")
284         heat_template.add_router("router1", "gw1", "subnet1")
285         heat_template.add_router_interface("router_if1", "router1", "subnet1")
286         network1 = mock.MagicMock()
287         network1.stack_name = "network1"
288         network1.subnet_stack_name = "subnet1"
289         network1.vnic_type = "normal"
290         network2 = mock.MagicMock()
291         network2.stack_name = "network2"
292         network2.subnet_stack_name = "subnet2"
293         network2.vnic_type = "normal"
294         heat_template.add_port("port1", network1)
295         heat_template.add_port("port2", network2,
296                                sec_group_id="sec_group1", provider="not-sriov")
297         heat_template.add_port("port3", network2,
298                                sec_group_id="sec_group1", provider="sriov")
299         heat_template.add_floating_ip("floating_ip1", "network1", "port1",
300                                       "router_if1")
301         heat_template.add_floating_ip("floating_ip2", "network2", "port2",
302                                       "router_if2", "foo-secgroup")
303         heat_template.add_floating_ip_association("floating_ip1_association",
304                                                   "floating_ip1", "port1")
305         heat_template.add_servergroup("server_grp2", "affinity")
306         heat_template.add_servergroup("server_grp3", "anti-affinity")
307         heat_template.add_security_group("security_group")
308         heat_template.add_server(name="server1", image="image1",
309                                  flavor="flavor1", flavors=[])
310         heat_template.add_server_group(name="servergroup",
311                                        policies=["policy1", "policy2"])
312         heat_template.add_server_group(name="servergroup",
313                                        policies="policy1")
314         heat_template.add_server(
315             name="server2", image="image1", flavor="flavor1", flavors=[],
316             ports=["port1", "port2"], networks=["network1", "network2"],
317             scheduler_hints="hints1", user="user1", key_name="foo-key",
318             user_data="user", metadata={"cat": 1, "doc": 2},
319             additional_properties={"prop1": 1, "prop2": 2})
320         heat_template.add_server(
321             name="server2", image="image1", flavor="flavor1",
322             flavors=["flavor1", "flavor2"], ports=["port1", "port2"],
323             networks=["network1", "network2"], scheduler_hints="hints1",
324             user="user1", key_name="foo-key", user_data="user",
325             metadata={"cat": 1, "doc": 2},
326             additional_properties={"prop1": 1, "prop2": 2})
327         heat_template.add_server(
328             name="server2", image="image1", flavor="flavor1",
329             flavors=["flavor3", "flavor4"], ports=["port1", "port2"],
330             networks=["network1", "network2"], scheduler_hints="hints1",
331             user="user1", key_name="foo-key", user_data="user",
332             metadata={"cat": 1, "doc": 2},
333             additional_properties={"prop1": 1, "prop2": 2})
334         heat_template.add_flavor(name="flavor1", vcpus=1, ram=2048, disk=1,
335                                  extra_specs={"cat": 1, "dog": 2})
336         heat_template.add_flavor(name=None, vcpus=1, ram=2048)
337         heat_template.add_server(
338             name="server1", image="image1", flavor="flavor1", flavors=[],
339             ports=["port1", "port2"], networks=["network1", "network2"],
340             scheduler_hints="hints1", user="user1", key_name="foo-key",
341             user_data="user", metadata={"cat": 1, "doc": 2},
342             additional_properties={"prop1": 1, "prop2": 2})
343         heat_template.add_network("network1")
344
345         heat_template.add_flavor("test")
346         self.assertEqual(heat_template.resources['test']['type'],
347                          'OS::Nova::Flavor')
348
349     def test_create_not_block(self):
350         heat_stack = mock.Mock()
351         with mock.patch.object(heat, 'HeatStack', return_value=heat_stack) \
352                 as mock_heatstack:
353             ret = self.template.create(block=False)
354
355         mock_heatstack.assert_called_once_with(
356             self.template.name, os_cloud_config=self.template._os_cloud_config)
357         heat_stack.create.assert_called_once_with(
358             self.template._template, self.template.heat_parameters, False,
359             3600)
360         self.assertEqual(heat_stack, ret)
361
362     def test_create_block(self):
363         heat_stack = mock.Mock()
364         heat_stack.status = self.template.HEAT_STATUS_COMPLETE
365         with mock.patch.object(heat, 'HeatStack', return_value=heat_stack):
366             ret = self.template.create(block=False)
367         heat_stack.create.assert_called_once_with(
368             self.template._template, self.template.heat_parameters, False,
369             3600)
370         self.assertEqual(heat_stack, ret)
371
372     def test_create_block_status_no_complete(self):
373         heat_stack = mock.Mock()
374         heat_stack.status = 'other status'
375         heat_stack.get_failures.return_value = []
376         with mock.patch.object(heat, 'HeatStack', return_value=heat_stack):
377             self.assertRaises(exceptions.HeatTemplateError,
378                               self.template.create, block=True)
379         heat_stack.create.assert_called_once_with(
380             self.template._template, self.template.heat_parameters, True,
381             3600)
382
383     def test_create_block_status_no_complete_with_reasons(self):
384         heat_stack = mock.Mock()
385         heat_stack.status = 'other status'
386         heat_stack.get_failures.return_value = [
387             mock.Mock(resource_status_reason="A reason"),
388             mock.Mock(resource_status_reason="Something else")
389         ]
390         with mock.patch.object(heat, 'HeatStack', return_value=heat_stack):
391             with mock.patch.object(heat, 'log') as mock_log:
392                 self.assertRaises(exceptions.HeatTemplateError,
393                                   self.template.create, block=True)
394                 mock_log.error.assert_any_call("%s", "A reason")
395                 mock_log.error.assert_any_call("%s", "Something else")
396         heat_stack.create.assert_called_once_with(
397             self.template._template, self.template.heat_parameters, True,
398             3600)