Merge "Fix up formatting on devguide"
[yardstick.git] / yardstick / tests / unit / benchmark / contexts / test_node.py
1 ##############################################################################
2 # Copyright (c) 2015-2017 Huawei Technologies Co.,Ltd and others.
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 os
11 import unittest
12 import errno
13 import mock
14
15 from yardstick.common import constants as consts
16 from yardstick.benchmark.contexts import base
17 from yardstick.benchmark.contexts import node
18 from yardstick.common import exceptions
19
20
21 class NodeContextTestCase(unittest.TestCase):
22
23     PREFIX = 'yardstick.benchmark.contexts.node'
24
25     NODES_SAMPLE = "nodes_sample.yaml"
26     NODES_DUPLICATE_SAMPLE = "nodes_duplicate_sample.yaml"
27
28     def setUp(self):
29         self.test_context = node.NodeContext()
30         self.addCleanup(self._remove_contexts)
31         self.os_path_join = os.path.join
32         self.attrs = {
33             'name': 'foo',
34             'task_id': '1234567890',
35             'file': self._get_file_abspath(self.NODES_SAMPLE)
36         }
37
38     @staticmethod
39     def _remove_contexts():
40         for context in base.Context.list:
41             context._delete_context()
42         base.Context.list = []
43
44     def _get_file_abspath(self, filename):
45         curr_path = os.path.dirname(os.path.abspath(__file__))
46         file_path = self.os_path_join(curr_path, filename)
47         return file_path
48
49     def test___init__(self):
50         self.assertIsNone(self.test_context._name)
51         self.assertIsNone(self.test_context.file_path)
52         self.assertEqual(self.test_context.nodes, [])
53         self.assertEqual(self.test_context.controllers, [])
54         self.assertEqual(self.test_context.computes, [])
55         self.assertEqual(self.test_context.baremetals, [])
56         self.assertEqual(self.test_context.env, {})
57         self.assertEqual(self.test_context.attrs, {})
58
59     @mock.patch('yardstick.common.utils.read_yaml_file')
60     @mock.patch('{}.os.path.join'.format(PREFIX))
61     def test_init_negative(self, mock_path_join, read_mock):
62         special_path = '/foo/bar/error_file'
63         error_path = self._get_file_abspath("error_file")
64
65         def path_join(*args):
66             if args == (consts.YARDSTICK_ROOT_PATH, error_path):
67                 return special_path
68             return self.os_path_join(*args)
69
70         # we can't count mock_path_join calls because
71         # it can catch join calls for .pyc files.
72         mock_path_join.side_effect = path_join
73         read_calls = 0
74
75         with self.assertRaises(KeyError):
76             self.test_context.init({})
77
78         self.assertEqual(read_mock.call_count, read_calls)
79
80         attrs = {
81             'name': 'foo',
82             'task_id': '1234567890',
83             'file': error_path,
84         }
85         read_mock.side_effect = IOError(errno.EBUSY, 'busy')
86         with self.assertRaises(IOError) as raised:
87             self.test_context.init(attrs)
88
89         read_calls += 1
90         self.assertEqual(read_mock.call_count, read_calls)
91         self.assertIn(attrs['file'], self.test_context.file_path)
92         self.assertEqual(raised.exception.errno, errno.EBUSY)
93         self.assertEqual(str(raised.exception), str(read_mock.side_effect))
94
95         read_mock.side_effect = IOError(errno.ENOENT, 'not found')
96         with self.assertRaises(IOError) as raised:
97             self.test_context.init(attrs)
98
99         read_calls += 2
100         self.assertEqual(read_mock.call_count, read_calls)
101         self.assertEqual(self.test_context.file_path, special_path)
102         self.assertEqual(raised.exception.errno, errno.ENOENT)
103         self.assertEqual(str(raised.exception), str(read_mock.side_effect))
104
105     def test__dispatch_script(self):
106         self.test_context.init(self.attrs)
107
108         self.test_context.env = {'bash': [{'script': 'dummy'}]}
109         self.test_context._execute_script = mock.Mock()
110         self.assertEqual(self.test_context._dispatch_script('bash'), None)
111
112     def test__dispatch_ansible(self):
113         self.test_context.init(self.attrs)
114
115         self.test_context.env = {'ansible': [{'script': 'dummy'}]}
116         self.test_context._do_ansible_job = mock.Mock()
117         self.assertEqual(self.test_context._dispatch_ansible('ansible'), None)
118         self.test_context.env = {}
119         self.assertEqual(self.test_context._dispatch_ansible('ansible'), None)
120
121     @mock.patch("{}.AnsibleCommon".format(PREFIX))
122     def test__do_ansible_job(self, *args):
123         self.assertIsNone(self.test_context._do_ansible_job('dummy'))
124
125     def test_init(self):
126         self.test_context.init(self.attrs)
127
128         self.assertEqual(self.test_context.name, "foo-12345678")
129         self.assertEqual(len(self.test_context.nodes), 4)
130         self.assertEqual(len(self.test_context.controllers), 2)
131         self.assertEqual(len(self.test_context.computes), 1)
132         self.assertEqual(self.test_context.computes[0]["name"], "node3")
133         self.assertEqual(len(self.test_context.baremetals), 1)
134         self.assertEqual(self.test_context.baremetals[0]["name"], "node4")
135
136     def test__get_server_with_dict_attr_name(self):
137         self.test_context.init(self.attrs)
138         result = self.test_context._get_server({'name': 'node1.foo-12345678'})
139
140         self.assertIsNone(result, None)
141
142     def test__get_server_not_found(self):
143         self.test_context.init(self.attrs)
144
145         self.assertIsNone(self.test_context._get_server('bar.foo-12345678'))
146
147     def test__get_server_mismatch(self):
148         self.test_context.init(self.attrs)
149
150         self.assertIsNone(self.test_context._get_server('bar.foo1'))
151
152     def test__get_server_duplicate(self):
153         self.attrs['file'] = self._get_file_abspath(
154             self.NODES_DUPLICATE_SAMPLE)
155         self.test_context.init(self.attrs)
156
157         with self.assertRaises(ValueError):
158             self.test_context._get_server('node1.foo-12345678')
159
160     def test__get_server_found(self):
161         self.test_context.init(self.attrs)
162
163         result = self.test_context._get_server('node1.foo-12345678')
164
165         self.assertEqual(result['ip'], '10.229.47.137')
166         self.assertEqual(result['name'], 'node1.foo-12345678')
167         self.assertEqual(result['user'], 'root')
168         self.assertEqual(result['key_filename'], '/root/.yardstick_key')
169
170     def test__get_physical_nodes(self):
171         self.test_context.init(self.attrs)
172         nodes = self.test_context._get_physical_nodes()
173         self.assertEqual(nodes, self.test_context.nodes)
174
175     def test__get_physical_node_for_server(self):
176         self.test_context.init(self.attrs)
177
178         # When server is not from this context
179         result = self.test_context._get_physical_node_for_server('node1.another-context')
180         self.assertIsNone(result)
181
182         # When node_name is not from this context
183         result = self.test_context._get_physical_node_for_server('fake.foo-12345678')
184         self.assertIsNone(result)
185
186         result = self.test_context._get_physical_node_for_server('node1.foo-12345678')
187         self.assertEqual(result, 'node1.foo')
188
189     def test_update_collectd_options_for_node(self):
190         self.test_context.init(self.attrs)
191         options = {'collectd': {'interval': 5}}
192
193         with self.assertRaises(exceptions.ContextUpdateCollectdForNodeError):
194             self.test_context.update_collectd_options_for_node(options, 'fake.foo-12345678')
195
196         self.test_context.update_collectd_options_for_node(options, 'node1.foo-12345678')
197
198         node_collectd_options = [node for node in self.test_context.nodes
199                                  if node['name'] == 'node1'][0]['collectd']
200
201         self.assertEqual(node_collectd_options, options)
202
203     @mock.patch('{}.NodeContext._dispatch_script'.format(PREFIX))
204     def test_deploy(self, dispatch_script_mock):
205         obj = node.NodeContext()
206         self.addCleanup(obj._delete_context)
207         obj.env = {
208             'type': 'script'
209         }
210         obj.deploy()
211         dispatch_script_mock.assert_called_once()
212
213     @mock.patch('{}.NodeContext._dispatch_ansible'.format(PREFIX))
214     def test_deploy_anisible(self, dispatch_ansible_mock):
215         obj = node.NodeContext()
216         self.addCleanup(obj._delete_context)
217         obj.env = {
218             'type': 'ansible'
219         }
220         obj.deploy()
221         dispatch_ansible_mock.assert_called_once()
222
223     @mock.patch('{}.NodeContext._dispatch_script'.format(PREFIX))
224     def test_undeploy(self, dispatch_script_mock):
225         obj = node.NodeContext()
226         obj.env = {
227             'type': 'script'
228         }
229         obj.undeploy()
230         dispatch_script_mock.assert_called_once()
231
232     @mock.patch('{}.NodeContext._dispatch_ansible'.format(PREFIX))
233     def test_undeploy_anisble(self, dispatch_ansible_mock):
234         obj = node.NodeContext()
235         obj.env = {
236             'type': 'ansible'
237         }
238         obj.undeploy()
239         dispatch_ansible_mock.assert_called_once()
240
241     @mock.patch('{}.ssh.SSH._put_file_shell'.format(PREFIX))
242     @mock.patch('{}.ssh.SSH.execute'.format(PREFIX))
243     def test_execute_remote_script(self, execute_mock, put_file_mock):
244         obj = node.NodeContext()
245         self.addCleanup(obj._delete_context)
246         obj.env = {'prefix': 'yardstick.benchmark.scenarios.compute'}
247         node_name_args = 'node5'
248         obj.nodes = [{
249             'name': node_name_args,
250             'user': 'ubuntu',
251             'ip': '10.10.10.10',
252             'pwd': 'ubuntu',
253         }]
254
255         info = {'script': 'computecapacity.bash'}
256         execute_mock.return_value = (0, '', '')
257         obj._execute_remote_script('node5', info)
258
259         put_file_mock.assert_called_once()
260         execute_mock.assert_called()
261
262     @mock.patch('{}.NodeContext._execute_local_script'.format(PREFIX))
263     def test_execute_script_local(self, local_execute_mock):
264         node_name = 'local'
265         info = {}
266         obj = node.NodeContext()
267         self.addCleanup(obj._delete_context)
268         obj._execute_script(node_name, info)
269         local_execute_mock.assert_called_once()
270
271     @mock.patch('{}.NodeContext._execute_remote_script'.format(PREFIX))
272     def test_execute_script_remote(self, remote_execute_mock):
273         node_name = 'node5'
274         info = {}
275         obj = node.NodeContext()
276         self.addCleanup(obj._delete_context)
277         obj._execute_script(node_name, info)
278         remote_execute_mock.assert_called_once()
279
280     def test_get_script(self):
281         script_args = 'hello.bash'
282         info_args = {
283             'script': script_args
284         }
285         obj = node.NodeContext()
286         self.addCleanup(obj._delete_context)
287         script, options = obj._get_script(info_args)
288         self.assertEqual(script_args, script)
289         self.assertEqual('', options)
290
291     def test_node_info(self):
292         node_name_args = 'node5'
293         obj = node.NodeContext()
294         self.addCleanup(obj._delete_context)
295         obj.nodes = [{'name': node_name_args, 'check': node_name_args}]
296         node_info = obj._get_node_info(node_name_args)
297         self.assertEqual(node_info.get('check'), node_name_args)
298
299     @mock.patch('{}.ssh.SSH.wait'.format(PREFIX))
300     def test_get_client(self, wait_mock):
301         node_name_args = 'node5'
302         obj = node.NodeContext()
303         self.addCleanup(obj._delete_context)
304         obj.nodes = [{
305             'name': node_name_args,
306             'user': 'ubuntu',
307             'ip': '10.10.10.10',
308             'pwd': 'ubuntu',
309         }]
310         obj._get_client(node_name_args)
311         wait_mock.assert_called_once()
312
313     def test_get_server(self):
314         self.test_context.init(self.attrs)
315         self.test_context._name = 'foo'
316         self.test_context._task_id = '1234567890'
317         self.test_context._name_task_id = '{}-{}'.format(
318             self.test_context._name, self.test_context._task_id[:8])
319         self.assertEqual('foo-12345678', self.test_context.name)
320         self.assertIsNotNone(self.test_context._task_id)
321
322         result = self.test_context.get_server('node1.foo-12345678')
323
324         self.assertEqual(result['ip'], '10.229.47.137')
325         self.assertEqual(result['name'], 'node1.foo-12345678')
326         self.assertEqual(result['user'], 'root')
327         self.assertEqual(result['key_filename'], '/root/.yardstick_key')
328
329     def test_get_server_server_not_in_context(self):
330         self.test_context.init(self.attrs)
331
332         with self.assertRaises(ValueError):
333             self.test_context.get_server('my2.foo-12345678')
334
335     def test_get_context_from_server(self):
336         self.test_context._name = 'vnf1'
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.nodes = [{'name': 'my', 'value': 100}]
341         self.test_context.attrs = {'attr1': 200}
342
343         self.assertIs(
344             self.test_context.get_context_from_server('my.vnf1-12345678'),
345             self.test_context)
346
347     # TODO: Split this into more granular tests
348     def test__get_network(self):
349         network1 = {
350             'name': 'net_1',
351             'vld_id': 'vld111',
352             'segmentation_id': 'seg54',
353             'network_type': 'type_a',
354             'physical_network': 'phys',
355         }
356         network2 = {
357             'name': 'net_2',
358             'vld_id': 'vld999',
359         }
360         self.test_context.networks = {
361             'a': network1,
362             'b': network2,
363         }
364
365         attr_name = {}
366         self.assertIsNone(self.test_context._get_network(attr_name))
367
368         attr_name = {'vld_id': 'vld777'}
369         self.assertIsNone(self.test_context._get_network(attr_name))
370
371         self.assertIsNone(self.test_context._get_network(None))
372
373         attr_name = 'vld777'
374         self.assertIsNone(self.test_context._get_network(attr_name))
375
376         attr_name = {'vld_id': 'vld999'}
377         expected = {
378             "name": 'net_2',
379             "vld_id": 'vld999',
380             "segmentation_id": None,
381             "network_type": None,
382             "physical_network": None,
383         }
384         result = self.test_context._get_network(attr_name)
385         self.assertDictEqual(result, expected)
386
387         attr_name = 'a'
388         expected = network1
389         result = self.test_context._get_network(attr_name)
390         self.assertDictEqual(result, expected)