Merge changes from topics 'feat/keep_vnf', 'YARDSTICK-886'
[yardstick.git] / yardstick / tests / unit / benchmark / contexts / test_heat.py
1 #!/usr/bin/env python
2
3 ##############################################################################
4 # Copyright (c) 2015 Ericsson AB and others.
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 from collections import OrderedDict
13 from itertools import count
14 import logging
15 import os
16 import uuid
17
18 import mock
19 import unittest
20
21 from yardstick.benchmark.contexts import base
22 from yardstick.benchmark.contexts import heat
23 from yardstick.benchmark.contexts import model
24 from yardstick.common import exceptions as y_exc
25 from yardstick.orchestrator import heat as orch_heat
26
27
28 LOG = logging.getLogger(__name__)
29
30
31 class HeatContextTestCase(unittest.TestCase):
32
33     def __init__(self, *args, **kwargs):
34         super(HeatContextTestCase, self).__init__(*args, **kwargs)
35         self.name_iter = ('vnf{:03}'.format(x) for x in count(0, step=3))
36
37     def setUp(self):
38         self.test_context = heat.HeatContext()
39         self.addCleanup(self._remove_contexts)
40         self.mock_context = mock.Mock(spec=heat.HeatContext())
41
42     def _remove_contexts(self):
43         if self.test_context in self.test_context.list:
44             self.test_context._delete_context()
45
46     def test___init__(self):
47         self.assertIsNone(self.test_context._name)
48         self.assertIsNone(self.test_context._task_id)
49         self.assertFalse(self.test_context._flags.no_setup)
50         self.assertFalse(self.test_context._flags.no_teardown)
51         self.assertIsNone(self.test_context.stack)
52         self.assertEqual(self.test_context.networks, OrderedDict())
53         self.assertEqual(self.test_context.servers, [])
54         self.assertEqual(self.test_context.placement_groups, [])
55         self.assertEqual(self.test_context.server_groups, [])
56         self.assertIsNone(self.test_context.keypair_name)
57         self.assertIsNone(self.test_context.secgroup_name)
58         self.assertEqual(self.test_context._server_map, {})
59         self.assertIsNone(self.test_context._image)
60         self.assertIsNone(self.test_context._flavor)
61         self.assertIsNone(self.test_context._user)
62         self.assertIsNone(self.test_context.template_file)
63         self.assertIsNone(self.test_context.heat_parameters)
64         self.assertIsNotNone(self.test_context.key_uuid)
65         self.assertIsNotNone(self.test_context.key_filename)
66
67     @mock.patch('yardstick.benchmark.contexts.heat.PlacementGroup')
68     @mock.patch('yardstick.benchmark.contexts.heat.ServerGroup')
69     @mock.patch('yardstick.benchmark.contexts.heat.Network')
70     @mock.patch('yardstick.benchmark.contexts.heat.Server')
71     def test_init(self, mock_server, mock_network, mock_sg, mock_pg):
72
73         pgs = {'pgrp1': {'policy': 'availability'}}
74         sgs = {'servergroup1': {'policy': 'affinity'}}
75         networks = {'bar': {'cidr': '10.0.1.0/24'}}
76         servers = {'baz': {'floating_ip': True, 'placement': 'pgrp1'}}
77         attrs = {'name': 'foo',
78                  'task_id': '1234567890',
79                  'placement_groups': pgs,
80                  'server_groups': sgs,
81                  'networks': networks,
82                  'servers': servers}
83
84         self.test_context.init(attrs)
85
86         self.assertFalse(self.test_context._flags.no_setup)
87         self.assertFalse(self.test_context._flags.no_teardown)
88         self.assertEqual(self.test_context._name, "foo")
89         self.assertEqual(self.test_context._task_id, '1234567890')
90         self.assertEqual(self.test_context.name, "foo-12345678")
91         self.assertEqual(self.test_context.keypair_name, "foo-12345678-key")
92         self.assertEqual(self.test_context.secgroup_name, "foo-12345678-secgroup")
93
94         mock_pg.assert_called_with('pgrp1', self.test_context,
95                                    pgs['pgrp1']['policy'])
96         mock_sg.assert_called_with('servergroup1', self.test_context,
97                                    sgs['servergroup1']['policy'])
98         self.assertEqual(len(self.test_context.placement_groups), 1)
99         self.assertEqual(len(self.test_context.server_groups), 1)
100
101         mock_network.assert_called_with(
102             'bar', self.test_context, networks['bar'])
103         self.assertEqual(len(self.test_context.networks), 1)
104
105         mock_server.assert_called_with('baz', self.test_context,
106                                        servers['baz'])
107         self.assertEqual(len(self.test_context.servers), 1)
108
109         if os.path.exists(self.test_context.key_filename):
110             try:
111                 os.remove(self.test_context.key_filename)
112                 os.remove(self.test_context.key_filename + ".pub")
113             except OSError:
114                 LOG.exception("key_filename: %s",
115                               self.test_context.key_filename)
116
117     def test_init_no_name_or_task_id(self):
118         attrs = {}
119         self.assertRaises(KeyError, self.test_context.init, attrs)
120
121     def test_name(self):
122         self.test_context._name = 'foo'
123         self.test_context._task_id = '1234567890'
124         self.test_context._name_task_id = '{}-{}'.format(
125             self.test_context._name, self.test_context._task_id[:8])
126         self.assertEqual(self.test_context.name, 'foo-12345678')
127         self.assertEqual(self.test_context.assigned_name, 'foo')
128
129     def test_name_flags(self):
130         self.test_context._flags = base.Flags(**{"no_setup": True, "no_teardown": True})
131         self.test_context._name = 'foo'
132         self.test_context._task_id = '1234567890'
133
134         self.assertEqual(self.test_context.name, 'foo')
135         self.assertEqual(self.test_context.assigned_name, 'foo')
136
137     @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
138     def test__add_resources_to_template_no_servers(self, mock_template):
139         self.test_context._name = 'ctx'
140         self.test_context._task_id = '1234567890'
141         self.test_context._name_task_id = '{}-{}'.format(
142             self.test_context._name, self.test_context._task_id[:8])
143         self.test_context.keypair_name = "ctx-key"
144         self.test_context.secgroup_name = "ctx-secgroup"
145         self.test_context.key_uuid = "2f2e4997-0a8e-4eb7-9fa4-f3f8fbbc393b"
146         netattrs = {'cidr': '10.0.0.0/24', 'provider': None,
147                     'external_network': 'ext_net'}
148         self.test_context.networks = OrderedDict(
149             {"mynet": model.Network("mynet", self.test_context,
150                                            netattrs)})
151
152         self.test_context._add_resources_to_template(mock_template)
153         mock_template.add_keypair.assert_called_with(
154             "ctx-key",
155             "2f2e4997-0a8e-4eb7-9fa4-f3f8fbbc393b")
156         mock_template.add_security_group.assert_called_with("ctx-secgroup")
157         mock_template.add_network.assert_called_with(
158             "ctx-12345678-mynet", 'physnet1', None, None, None, None)
159         mock_template.add_router.assert_called_with(
160             "ctx-12345678-mynet-router",
161             netattrs["external_network"],
162             "ctx-12345678-mynet-subnet")
163         mock_template.add_router_interface.assert_called_with(
164             "ctx-12345678-mynet-router-if0",
165             "ctx-12345678-mynet-router",
166             "ctx-12345678-mynet-subnet")
167
168     @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
169     def test_attrs_get(self, *args):
170         image, flavor, user = expected_tuple = 'foo1', 'foo2', 'foo3'
171         self.assertNotEqual(self.test_context.image, image)
172         self.assertNotEqual(self.test_context.flavor, flavor)
173         self.assertNotEqual(self.test_context.user, user)
174         self.test_context._image = image
175         self.test_context._flavor = flavor
176         self.test_context._user = user
177         attr_tuple = self.test_context.image, self.test_context.flavor, self.test_context.user
178         self.assertEqual(attr_tuple, expected_tuple)
179
180     @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
181     def test_attrs_set_negative(self, *args):
182         with self.assertRaises(AttributeError):
183             self.test_context.image = 'foo'
184
185         with self.assertRaises(AttributeError):
186             self.test_context.flavor = 'foo'
187
188         with self.assertRaises(AttributeError):
189             self.test_context.user = 'foo'
190
191     def test__create_new_stack(self):
192         template = mock.Mock()
193         self.test_context._create_new_stack(template)
194         template.create.assert_called_once()
195
196     def test__create_new_stack_stack_create_failed(self):
197         template = mock.Mock()
198         template.create.side_effect = y_exc.HeatTemplateError
199
200         self.assertRaises(y_exc.HeatTemplateError,
201                           self.test_context._create_new_stack,
202                           template)
203
204     def test__create_new_stack_keyboard_interrupt(self):
205         template = mock.Mock()
206         template.create.side_effect = KeyboardInterrupt
207         self.assertRaises(y_exc.StackCreationInterrupt,
208                           self.test_context._create_new_stack,
209                           template)
210
211     @mock.patch.object(orch_heat.HeatTemplate, 'add_keypair')
212     @mock.patch.object(heat.HeatContext, '_create_new_stack')
213     def test_deploy_stack_creation_failed(self, mock_create, *args):
214         self.test_context._name = 'foo'
215         self.test_context._task_id = '1234567890'
216         self.test_context._name_task_id = 'foo-12345678'
217         mock_create.side_effect = y_exc.HeatTemplateError
218         self.assertRaises(y_exc.HeatTemplateError,
219                           self.test_context.deploy)
220
221     @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
222     def test_deploy(self, mock_template):
223         self.test_context._name = 'foo'
224         self.test_context._task_id = '1234567890'
225         self.test_context._name_task_id = '{}-{}'.format(
226             self.test_context._name, self.test_context._task_id[:8])
227         self.test_context.template_file = '/bar/baz/some-heat-file'
228         self.test_context.heat_parameters = {'image': 'cirros'}
229         self.test_context.get_neutron_info = mock.MagicMock()
230         self.test_context.deploy()
231
232         mock_template.assert_called_with('foo-12345678',
233                                          '/bar/baz/some-heat-file',
234                                          {'image': 'cirros'})
235         self.assertIsNotNone(self.test_context.stack)
236
237     def test_add_server_port(self):
238         network1 = mock.MagicMock()
239         network2 = mock.MagicMock()
240         self.test_context._name = 'foo'
241         self.test_context._task_id = '1234567890'
242         self.test_context._name_task_id = '{}-{}'.format(
243             self.test_context._name, self.test_context._task_id[:8])
244         self.test_context.stack = mock.MagicMock()
245         self.test_context.networks = {
246             'a': network1,
247             'c': network2,
248         }
249         self.test_context.stack.outputs = {
250             u'b': u'10.20.30.45',
251             u'b-subnet_id': 1,
252             u'foo-12345678-a-subnet-cidr': u'10.20.0.0/15',
253             u'foo-12345678-a-subnet-gateway_ip': u'10.20.30.1',
254             u'b-mac_address': u'00:01',
255             u'b-device_id': u'dev21',
256             u'b-network_id': u'net789',
257             u'd': u'40.30.20.15',
258             u'd-subnet_id': 2,
259             u'foo-12345678-c-subnet-cidr': u'40.30.0.0/18',
260             u'foo-12345678-c-subnet-gateway_ip': u'40.30.20.254',
261             u'd-mac_address': u'00:10',
262             u'd-device_id': u'dev43',
263             u'd-network_id': u'net987',
264             u'e': u'40.30.20.15',
265             u'e-subnet_id': 2,
266             u'e-mac_address': u'00:10',
267             u'e-device_id': u'dev43',
268             u'e-network_id': u'net987',
269         }
270         server = mock.MagicMock()
271         server.ports = OrderedDict([
272             ('a', [{'stack_name': 'b', 'port': 'port_a'}]),
273             ('c', [{'stack_name': 'd', 'port': 'port_c'},
274                    {'stack_name': 'e', 'port': 'port_f'}]),
275         ])
276
277         expected = {
278             "private_ip": '10.20.30.45',
279             "subnet_id": 1,
280             "subnet_cidr": '10.20.0.0/15',
281             "network": '10.20.0.0',
282             "netmask": '255.254.0.0',
283             "name": "port_a",
284             "gateway_ip": '10.20.30.1',
285             "mac_address": '00:01',
286             "device_id": 'dev21',
287             "network_id": 'net789',
288             "network_name": 'a',
289             "local_mac": '00:01',
290             "local_ip": '10.20.30.45',
291         }
292         self.test_context.add_server_port(server)
293         self.assertEqual(server.private_ip, '10.20.30.45')
294         self.assertEqual(len(server.interfaces), 3)
295         self.assertDictEqual(server.interfaces['port_a'], expected)
296
297     @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
298     def test_undeploy(self, mock_template):
299         self.test_context.stack = mock_template
300         self.test_context._name = 'foo'
301         self.test_context._task_id = '1234567890'
302         self.test_context._name_task_id = '{}-{}'.format(
303             self.test_context._name, self.test_context._task_id[:8])
304         self.test_context.undeploy()
305         self.assertTrue(mock_template.delete.called)
306
307     @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
308     @mock.patch('yardstick.benchmark.contexts.heat.os')
309     def test_undeploy_key_filename(self, mock_os, mock_template):
310         self.test_context.stack = mock_template
311         self.test_context._name = 'foo'
312         self.test_context._task_id = '1234567890'
313         self.test_context._name_task_id = '{}-{}'.format(
314             self.test_context._name, self.test_context._task_id)
315         mock_os.path.exists.return_value = True
316         self.assertIsNone(self.test_context.undeploy())
317
318     @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources")
319     def test__get_server_found_dict(self, *args):
320         """
321         Use HeatContext._get_server to get a server that matches
322         based on a dictionary input.
323         """
324         foo2_server = mock.Mock()
325         foo2_server.key_filename = None
326         foo2_server.private_ip = '10.0.0.2'
327         foo2_server.public_ip = '127.0.0.2'
328         foo2_server.context.user = 'oof'
329
330         baz3_server = mock.Mock()
331         baz3_server.key_filename = None
332         baz3_server.private_ip = '10.0.0.3'
333         baz3_server.public_ip = '127.0.0.3'
334         baz3_server.context.user = 'zab'
335
336         self.test_context._name = 'bar'
337         self.test_context._task_id = '1234567890'
338         self.test_context._name_task_id = '{}-{}'.format(
339             self.test_context._name, self.test_context._task_id[:8])
340         self.test_context._user = 'bot'
341         self.test_context.stack = mock.Mock()
342         self.test_context.stack.outputs = {
343             'private_ip': '10.0.0.1',
344             'public_ip': '127.0.0.1',
345         }
346         self.test_context.key_uuid = uuid.uuid4()
347         self.test_context._server_map = {
348             'baz3': baz3_server,
349             'foo2': foo2_server,
350         }
351
352         attr_name = {
353             'name': 'foo.bar-12345678',
354             'private_ip_attr': 'private_ip',
355             'public_ip_attr': 'public_ip',
356         }
357         result = self.test_context._get_server(attr_name)
358         self.assertEqual(result['user'], 'bot')
359         self.assertEqual(result['ip'], '127.0.0.1')
360         self.assertEqual(result['private_ip'], '10.0.0.1')
361
362     @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources")
363     def test__get_server_found_dict_no_attrs(self, *args):
364         """
365         Use HeatContext._get_server to get a server that matches
366         based on a dictionary input.
367         """
368         foo2_server = mock.Mock()
369         foo2_server.private_ip = '10.0.0.2'
370         foo2_server.public_ip = '127.0.0.2'
371         foo2_server.context.user = 'oof'
372
373         baz3_server = mock.Mock()
374         baz3_server.private_ip = '10.0.0.3'
375         baz3_server.public_ip = '127.0.0.3'
376         baz3_server.context.user = 'zab'
377
378         self.test_context._name = 'bar'
379         self.test_context._task_id = '1234567890'
380         self.test_context._name_task_id = '{}-{}'.format(
381             self.test_context._name, self.test_context._task_id[:8])
382         self.test_context._user = 'bot'
383         self.test_context.stack = mock.Mock()
384         self.test_context.stack.outputs = {
385             'private_ip': '10.0.0.1',
386             'public_ip': '127.0.0.1',
387         }
388         self.test_context.key_uuid = uuid.uuid4()
389         self.test_context._server_map = {
390             'baz3': baz3_server,
391             'foo2': foo2_server,
392         }
393
394         attr_name = {
395             'name': 'foo.bar-12345678',
396         }
397         result = self.test_context._get_server(attr_name)
398         self.assertEqual(result['user'], 'bot')
399         # no private ip attr mapping in the map results in None value in the result
400         self.assertIsNone(result['private_ip'])
401         # no public ip attr mapping in the map results in no value in the result
402         self.assertNotIn('ip', result)
403
404     @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources")
405     def test__get_server_found_not_dict(self, *args):
406         """
407         Use HeatContext._get_server to get a server that matches
408         based on a non-dictionary input
409         """
410         foo2_server = mock.Mock()
411         foo2_server.private_ip = '10.0.0.2'
412         foo2_server.public_ip = '127.0.0.2'
413         foo2_server.context.user = 'oof'
414
415         baz3_server = mock.Mock()
416         baz3_server.private_ip = '10.0.0.3'
417         baz3_server.public_ip = None
418         baz3_server.context.user = 'zab'
419
420         self.test_context._name = 'bar1'
421         self.test_context.stack = mock.Mock()
422         self.test_context.stack.outputs = {
423             'private_ip': '10.0.0.1',
424             'public_ip': '127.0.0.1',
425         }
426         self.test_context.key_uuid = uuid.uuid4()
427         self.test_context.generate_routing_table = mock.MagicMock(return_value=[])
428
429         self.test_context._server_map = {
430             'baz3': baz3_server,
431             'foo2': foo2_server,
432         }
433
434         attr_name = 'baz3'
435         result = self.test_context._get_server(attr_name)
436         self.assertEqual(result['user'], 'zab')
437         self.assertEqual(result['private_ip'], '10.0.0.3')
438         # no public_ip on the server results in no value in the result
439         self.assertNotIn('public_ip', result)
440
441     @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources")
442     def test__get_server_none_found_not_dict(self, *args):
443         """
444         Use HeatContext._get_server to not get a server due to
445         None value associated with the match to a non-dictionary
446         input
447         """
448         foo2_server = mock.Mock()
449         foo2_server.private_ip = '10.0.0.2'
450         foo2_server.public_ip = '127.0.0.2'
451         foo2_server.context.user = 'oof'
452
453         baz3_server = mock.Mock()
454         baz3_server.private_ip = '10.0.0.3'
455         baz3_server.public_ip = None
456         baz3_server.context.user = 'zab'
457
458         self.test_context._name = 'bar1'
459         self.test_context.stack = mock.Mock()
460         self.test_context.stack.outputs = {
461             'private_ip': '10.0.0.1',
462             'public_ip': '127.0.0.1',
463         }
464         self.test_context.key_uuid = uuid.uuid4()
465         self.test_context._server_map = {
466             'baz3': baz3_server,
467             'foo2': foo2_server,
468             'wow4': None,
469         }
470
471         attr_name = 'wow4'
472         result = self.test_context._get_server(attr_name)
473         self.assertIsNone(result)
474
475     @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources")
476     def test__get_server_not_found_dict(self, *args):
477         """
478         Use HeatContext._get_server to not get a server for lack
479         of a match to a dictionary input
480         """
481         foo2_server = mock.Mock()
482         foo2_server.private_ip = '10.0.0.2'
483         foo2_server.public_ip = '127.0.0.2'
484         foo2_server.context.user = 'oof'
485
486         baz3_server = mock.Mock()
487         baz3_server.private_ip = '10.0.0.3'
488         baz3_server.public_ip = None
489         baz3_server.context.user = 'zab'
490
491         self.test_context._name = 'bar1'
492         self.test_context._task_id = '1235467890'
493         self.test_context._name_task_id = '{}-{}'.format(
494             self.test_context._name, self.test_context._task_id[:8])
495         self.test_context.stack = mock.Mock()
496         self.test_context.stack.outputs = {
497             'private_ip': '10.0.0.1',
498             'public_ip': '127.0.0.1',
499         }
500         self.test_context.key_uuid = uuid.uuid4()
501         self.test_context._server_map = {
502             'baz3': baz3_server,
503             'foo2': foo2_server,
504         }
505
506         attr_name = {
507             'name': 'foo.wow4',
508             'private_ip_attr': 'private_ip',
509             'public_ip_attr': 'public_ip',
510         }
511         result = self.test_context._get_server(attr_name)
512         self.assertIsNone(result)
513
514     @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources")
515     def test__get_server_not_found_not_dict(self, *args):
516         """
517         Use HeatContext._get_server to not get a server for lack
518         of a match to a non-dictionary input
519         """
520         foo2_server = mock.Mock()
521         foo2_server.private_ip = '10.0.0.2'
522         foo2_server.public_ip = '127.0.0.2'
523         foo2_server.context.user = 'oof'
524
525         baz3_server = mock.Mock()
526         baz3_server.private_ip = '10.0.0.3'
527         baz3_server.public_ip = None
528         baz3_server.context.user = 'zab'
529
530         self.mock_context._name = 'bar1'
531         self.test_context.stack = mock.Mock()
532         self.mock_context.stack.outputs = {
533             'private_ip': '10.0.0.1',
534             'public_ip': '127.0.0.1',
535         }
536         self.mock_context.key_uuid = uuid.uuid4()
537         self.mock_context._server_map = {
538             'baz3': baz3_server,
539             'foo2': foo2_server,
540         }
541
542         attr_name = 'foo.wow4'
543         result = self.test_context._get_server(attr_name)
544         self.assertIsNone(result)
545
546     # TODO: Split this into more granular tests
547     def test__get_network(self):
548         network1 = mock.MagicMock()
549         network1.name = 'net_1'
550         network1.vld_id = 'vld111'
551         network1.segmentation_id = 'seg54'
552         network1.network_type = 'type_a'
553         network1.physical_network = 'phys'
554
555         network2 = mock.MagicMock()
556         network2.name = 'net_2'
557         network2.segmentation_id = 'seg45'
558         network2.network_type = 'type_b'
559         network2.physical_network = 'virt'
560
561         self.test_context.networks = {
562             'a': network1,
563             'b': network2,
564         }
565
566         attr_name = None
567         self.assertIsNone(self.test_context._get_network(attr_name))
568
569         attr_name = {}
570         self.assertIsNone(self.test_context._get_network(attr_name))
571
572         attr_name = {'network_type': 'nosuch'}
573         self.assertIsNone(self.test_context._get_network(attr_name))
574
575         attr_name = 'vld777'
576         self.assertIsNone(self.test_context._get_network(attr_name))
577
578         attr_name = {'segmentation_id': 'seg45'}
579         expected = {
580             "name": 'net_2',
581             "segmentation_id": 'seg45',
582             "network_type": 'type_b',
583             "physical_network": 'virt',
584         }
585         result = self.test_context._get_network(attr_name)
586         self.assertDictEqual(result, expected)
587
588         attr_name = 'a'
589         expected = {
590             "name": 'net_1',
591             "segmentation_id": 'seg54',
592             "network_type": 'type_a',
593             "physical_network": 'phys',
594         }
595         result = self.test_context._get_network(attr_name)
596         self.assertDictEqual(result, expected)