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