Merge "Add scale out TCs with availability zone support"
[yardstick.git] / yardstick / tests / unit / benchmark / contexts / standalone / test_ovs_dpdk.py
1 # Copyright (c) 2016-2017 Intel Corporation
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import io
16 import os
17
18 import mock
19 import six
20 import unittest
21
22 from yardstick.benchmark.contexts import base
23 from yardstick.benchmark.contexts.standalone import model
24 from yardstick.benchmark.contexts.standalone import ovs_dpdk
25 from yardstick.common import exceptions
26 from yardstick.network_services import utils
27
28
29 class OvsDpdkContextTestCase(unittest.TestCase):
30
31     NODES_SAMPLE = "nodes_sample.yaml"
32     NODES_ovs_dpdk_SAMPLE = "nodes_ovs_dpdk_sample.yaml"
33     NODES_DUPLICATE_SAMPLE = "nodes_duplicate_sample.yaml"
34
35     NETWORKS = {
36         'private_0': {
37             'phy_port': "0000:05:00.0",
38             'vpci': "0000:00:07.0",
39             'cidr': '152.16.100.10/24',
40             'interface': 'if0',
41             'mac': "00:00:00:00:00:01",
42             'vf_pci': {'vf_pci': 0},
43             'gateway_ip': '152.16.100.20'},
44         'public_0': {
45             'phy_port': "0000:05:00.1",
46             'vpci': "0000:00:08.0",
47             'cidr': '152.16.40.10/24',
48             'interface': 'if0',
49             'vf_pci': {'vf_pci': 0},
50             'mac': "00:00:00:00:00:01",
51             'gateway_ip': '152.16.100.20'},
52     }
53
54     def setUp(self):
55         self.attrs = {
56             'name': 'foo',
57             'task_id': '1234567890',
58             'file': self._get_file_abspath(self.NODES_ovs_dpdk_SAMPLE)
59         }
60         self.ovs_dpdk = ovs_dpdk.OvsDpdkContext()
61         self.addCleanup(self._remove_contexts)
62
63     @staticmethod
64     def _remove_contexts():
65         for context in base.Context.list:
66             context._delete_context()
67         base.Context.list = []
68
69     @mock.patch('yardstick.benchmark.contexts.standalone.model.Server')
70     @mock.patch('yardstick.benchmark.contexts.standalone.model.StandaloneContextHelper')
71     def test___init__(self, mock_helper, mock_server):
72         self.ovs_dpdk.helper = mock_helper
73         self.ovs_dpdk.vnf_node = mock_server
74         self.assertIsNone(self.ovs_dpdk.file_path)
75         self.assertTrue(self.ovs_dpdk.first_run)
76
77     def test_init(self):
78         ATTRS = {
79             'name': 'StandaloneOvsDpdk',
80             'task_id': '1234567890',
81             'file': 'pod',
82             'flavor': {},
83             'servers': {},
84             'networks': {},
85         }
86
87         self.ovs_dpdk.helper.parse_pod_file = mock.Mock(
88             return_value=[{}, {}, {}])
89         self.assertIsNone(self.ovs_dpdk.init(ATTRS))
90
91     def test_setup_ovs(self):
92         fake_path = '/fake_path'
93         fake_dpdk_nic_bind = 'dpdk_tool.py'
94         self.ovs_dpdk.ovs_properties = {'vpath': fake_path}
95         self.ovs_dpdk.dpdk_devbind = fake_dpdk_nic_bind
96         self.ovs_dpdk.networks = self.NETWORKS
97         self.ovs_dpdk.connection = mock.Mock()
98         self.ovs_dpdk.connection.execute = mock.Mock(return_value=(0, 0, 0))
99         create_from = fake_path + '/etc/openvswitch/conf.db'
100         create_to = fake_path + '/share/openvswitch/vswitch.ovsschema'
101         cmd_list = [
102             'killall -r "ovs.*" -q | true',
103             'mkdir -p {0}/etc/openvswitch'.format(fake_path),
104             'mkdir -p {0}/var/run/openvswitch'.format(fake_path),
105             'rm {0}/etc/openvswitch/conf.db | true'.format(fake_path),
106             'ovsdb-tool create {0} {1}'.format(create_from, create_to),
107             'modprobe vfio-pci',
108             'chmod a+x /dev/vfio',
109             'chmod 0666 /dev/vfio/*',
110             '{0} --force -b vfio-pci {1}'.format(fake_dpdk_nic_bind,
111                 self.ovs_dpdk.networks['private_0']['phy_port']),
112             '{0} --force -b vfio-pci {1}'.format(fake_dpdk_nic_bind,
113                 self.ovs_dpdk.networks['public_0']['phy_port'])
114         ]
115         calls = [mock.call(cmd, timeout=self.ovs_dpdk.CMD_TIMEOUT)
116                  for cmd in cmd_list]
117
118         self.ovs_dpdk.setup_ovs()
119         self.ovs_dpdk.connection.execute.assert_has_calls(calls,
120                                                           any_order=True)
121
122     def test_setup_ovs_exception(self):
123         self.ovs_dpdk.networks = self.NETWORKS
124         self.ovs_dpdk.connection = mock.Mock()
125         self.ovs_dpdk.connection.execute = mock.Mock(return_value=(1, 0, 0))
126
127         with self.assertRaises(exceptions.OVSSetupError):
128             self.ovs_dpdk.setup_ovs()
129
130     def test_start_ovs_serverswitch(self):
131         with mock.patch("yardstick.ssh.SSH") as ssh:
132             ssh_mock = mock.Mock(autospec=ssh.SSH)
133             ssh_mock.execute = \
134                 mock.Mock(return_value=(0, "a", ""))
135             ssh.return_value = ssh_mock
136             self.ovs_dpdk.connection = ssh_mock
137             self.ovs_dpdk.networks = self.NETWORKS
138             self.ovs_dpdk.ovs_properties = {}
139             self.ovs_dpdk.wait_for_vswitchd = 0
140             self.assertIsNone(self.ovs_dpdk.start_ovs_serverswitch())
141
142     def test_setup_ovs_bridge_add_flows(self):
143         with mock.patch("yardstick.ssh.SSH") as ssh:
144             ssh_mock = mock.Mock(autospec=ssh.SSH)
145             ssh_mock.execute = \
146                 mock.Mock(return_value=(0, "a", ""))
147             ssh.return_value = ssh_mock
148             self.ovs_dpdk.connection = ssh_mock
149             self.ovs_dpdk.networks = self.NETWORKS
150             self.ovs_dpdk.ovs_properties = {
151                 'version': {'ovs': '2.7.0'}
152             }
153             self.ovs_dpdk.wait_for_vswitchd = 0
154             self.assertIsNone(self.ovs_dpdk.setup_ovs_bridge_add_flows())
155
156     @mock.patch("yardstick.ssh.SSH")
157     def test_cleanup_ovs_dpdk_env(self, mock_ssh):
158        mock_ssh.execute.return_value = 0, "a", ""
159        self.ovs_dpdk.connection = mock_ssh
160        self.ovs_dpdk.networks = self.NETWORKS
161        self.ovs_dpdk.ovs_properties = {
162            'version': {'ovs': '2.7.0'}
163        }
164        self.ovs_dpdk.wait_for_vswitchd = 0
165        self.assertIsNone(self.ovs_dpdk.cleanup_ovs_dpdk_env())
166
167     @mock.patch.object(ovs_dpdk.OvsDpdkContext, '_check_hugepages')
168     @mock.patch.object(utils, 'get_nsb_option')
169     @mock.patch.object(model.OvsDeploy, 'ovs_deploy')
170     def test_check_ovs_dpdk_env(self, mock_ovs_deploy, mock_get_nsb_option,
171                                 mock_check_hugepages):
172         self.ovs_dpdk.connection = mock.Mock()
173         self.ovs_dpdk.connection.execute = mock.Mock(
174             return_value=(1, 0, 0))
175         self.ovs_dpdk.networks = self.NETWORKS
176         self.ovs_dpdk.ovs_properties = {
177             'version': {'ovs': '2.7.0', 'dpdk': '16.11.1'}
178         }
179         self.ovs_dpdk.wait_for_vswitchd = 0
180         self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock()
181         mock_get_nsb_option.return_value = 'fake_path'
182
183         self.ovs_dpdk.check_ovs_dpdk_env()
184         mock_ovs_deploy.assert_called_once()
185         mock_check_hugepages.assert_called_once()
186         mock_get_nsb_option.assert_called_once_with('bin_path')
187
188     @mock.patch.object(ovs_dpdk.OvsDpdkContext, '_check_hugepages')
189     def test_check_ovs_dpdk_env_wrong_version(self, mock_check_hugepages):
190         self.ovs_dpdk.connection = mock.Mock()
191         self.ovs_dpdk.connection.execute = mock.Mock(
192             return_value=(1, 0, 0))
193         self.ovs_dpdk.networks = self.NETWORKS
194         self.ovs_dpdk.ovs_properties = {
195             'version': {'ovs': '0.0.1', 'dpdk': '9.8.7'}
196         }
197         self.ovs_dpdk.wait_for_vswitchd = 0
198         self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock()
199
200         with self.assertRaises(exceptions.OVSUnsupportedVersion):
201             self.ovs_dpdk.check_ovs_dpdk_env()
202         mock_check_hugepages.assert_called_once()
203
204     @mock.patch('yardstick.ssh.SSH')
205     def test_deploy(self, *args):
206         self.ovs_dpdk.vm_deploy = False
207         self.assertIsNone(self.ovs_dpdk.deploy())
208
209         self.ovs_dpdk.vm_deploy = True
210         self.ovs_dpdk.host_mgmt = {}
211         self.ovs_dpdk.install_req_libs = mock.Mock()
212         self.ovs_dpdk.helper.get_nic_details = mock.Mock(return_value={})
213         self.ovs_dpdk.check_ovs_dpdk_env = mock.Mock(return_value={})
214         self.ovs_dpdk.setup_ovs = mock.Mock(return_value={})
215         self.ovs_dpdk.start_ovs_serverswitch = mock.Mock(return_value={})
216         self.ovs_dpdk.setup_ovs_bridge_add_flows = mock.Mock(return_value={})
217         self.ovs_dpdk.setup_ovs_dpdk_context = mock.Mock(return_value={})
218         self.ovs_dpdk.wait_for_vnfs_to_start = mock.Mock(return_value={})
219         # TODO(elfoley): This test should check states/sideeffects instead of
220         # output.
221         self.assertIsNone(self.ovs_dpdk.deploy())
222
223     @mock.patch.object(model.Libvirt, 'check_if_vm_exists_and_delete')
224     def test_undeploy(self, mock_libvirt):
225         self.ovs_dpdk.vm_deploy = True
226         self.ovs_dpdk.connection = mock.Mock()
227         self.ovs_dpdk.vm_names = ['vm_0', 'vm_1']
228         self.ovs_dpdk.drivers = ['vm_0', 'vm_1']
229         self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock()
230         self.ovs_dpdk.networks = self.NETWORKS
231         self.ovs_dpdk.undeploy()
232         mock_libvirt.assert_has_calls([
233             mock.call(self.ovs_dpdk.vm_names[0], self.ovs_dpdk.connection),
234             mock.call(self.ovs_dpdk.vm_names[1], self.ovs_dpdk.connection)
235         ])
236
237     def _get_file_abspath(self, filename):
238         curr_path = os.path.dirname(os.path.abspath(__file__))
239         file_path = os.path.join(curr_path, filename)
240         return file_path
241
242     def test__get_server_with_dic_attr_name(self):
243
244         self.ovs_dpdk.init(self.attrs)
245
246         attr_name = {'name': 'foo.bar'}
247         result = self.ovs_dpdk._get_server(attr_name)
248
249         self.assertEqual(result, None)
250
251     def test__get_server_not_found(self):
252
253         self.ovs_dpdk.helper.parse_pod_file = mock.Mock(
254             return_value=[{}, {}, {}])
255         self.ovs_dpdk.init(self.attrs)
256
257         attr_name = 'bar.foo'
258         result = self.ovs_dpdk._get_server(attr_name)
259
260         self.assertEqual(result, None)
261
262     def test__get_server_mismatch(self):
263
264         self.ovs_dpdk.init(self.attrs)
265
266         attr_name = 'bar.foo1'
267         result = self.ovs_dpdk._get_server(attr_name)
268
269         self.assertEqual(result, None)
270
271     def test__get_server_duplicate(self):
272
273         self.attrs['file'] = self._get_file_abspath(self.NODES_DUPLICATE_SAMPLE)
274
275         self.ovs_dpdk.init(self.attrs)
276
277         attr_name = 'node1.foo-12345678'
278         with self.assertRaises(ValueError):
279             self.ovs_dpdk._get_server(attr_name)
280
281     def test__get_server_found(self):
282
283         self.ovs_dpdk.init(self.attrs)
284
285         attr_name = 'node1.foo-12345678'
286         result = self.ovs_dpdk._get_server(attr_name)
287
288         self.assertEqual(result['ip'], '10.229.47.137')
289         self.assertEqual(result['name'], 'node1.foo-12345678')
290         self.assertEqual(result['user'], 'root')
291         self.assertEqual(result['key_filename'], '/root/.yardstick_key')
292
293     # TODO(elfoley): Split this test for networks that exist and networks that
294     #                don't
295     def test__get_network(self):
296         network1 = {
297             'name': 'net_1',
298             'vld_id': 'vld111',
299             'segmentation_id': 'seg54',
300             'network_type': 'type_a',
301             'physical_network': 'phys',
302         }
303         network2 = {
304             'name': 'net_2',
305             'vld_id': 'vld999',
306         }
307         self.ovs_dpdk.networks = {
308             'a': network1,
309             'b': network2,
310         }
311
312         # Tests for networks that do not exist
313         attr_name = {}
314         self.assertIsNone(self.ovs_dpdk._get_network(attr_name))
315
316         attr_name = {'vld_id': 'vld777'}
317         self.assertIsNone(self.ovs_dpdk._get_network(attr_name))
318
319         self.assertIsNone(self.ovs_dpdk._get_network(None))
320
321         # TODO(elfoley): Split this test
322         attr_name = 'vld777'
323         self.assertIsNone(self.ovs_dpdk._get_network(attr_name))
324
325         # Tests for networks that exist
326         attr_name = {'vld_id': 'vld999'}
327         expected = {
328             "name": 'net_2',
329             "vld_id": 'vld999',
330             "segmentation_id": None,
331             "network_type": None,
332             "physical_network": None,
333         }
334         result = self.ovs_dpdk._get_network(attr_name)
335         self.assertDictEqual(result, expected)
336
337         attr_name = 'a'
338         expected = network1
339         result = self.ovs_dpdk._get_network(attr_name)
340         self.assertDictEqual(result, expected)
341
342     def test_configure_nics_for_ovs_dpdk(self):
343         with mock.patch("yardstick.ssh.SSH") as ssh:
344             ssh_mock = mock.Mock(autospec=ssh.SSH)
345             ssh_mock.execute = \
346                 mock.Mock(return_value=(0, "a", ""))
347             ssh.return_value = ssh_mock
348         self.ovs_dpdk.vm_deploy = True
349         self.ovs_dpdk.connection = ssh_mock
350         self.ovs_dpdk.vm_names = ['vm_0', 'vm_1']
351         self.ovs_dpdk.drivers = []
352         self.ovs_dpdk.networks = self.NETWORKS
353         self.ovs_dpdk.helper.get_mac_address = mock.Mock(return_value="")
354         self.ovs_dpdk.get_vf_datas = mock.Mock(return_value="")
355         self.assertIsNone(self.ovs_dpdk.configure_nics_for_ovs_dpdk())
356
357     @mock.patch.object(model.Libvirt, 'add_ovs_interface')
358     def test__enable_interfaces(self, mock_add_ovs_interface):
359         self.ovs_dpdk.vm_deploy = True
360         self.ovs_dpdk.connection = mock.Mock()
361         self.ovs_dpdk.vm_names = ['vm_0', 'vm_1']
362         self.ovs_dpdk.drivers = []
363         self.ovs_dpdk.networks = self.NETWORKS
364         self.ovs_dpdk.ovs_properties = {'vpath': 'fake_path'}
365         self.ovs_dpdk.get_vf_datas = mock.Mock(return_value="")
366         self.ovs_dpdk._enable_interfaces(0, ["private_0"], 'test')
367         mock_add_ovs_interface.assert_called_once_with(
368             'fake_path', 0, self.NETWORKS['private_0']['vpci'],
369             self.NETWORKS['private_0']['mac'], 'test')
370
371     @mock.patch.object(model.Libvirt, 'write_file')
372     @mock.patch.object(model.Libvirt, 'build_vm_xml')
373     @mock.patch.object(model.Libvirt, 'check_if_vm_exists_and_delete')
374     @mock.patch.object(model.Libvirt, 'virsh_create_vm')
375     def test_setup_ovs_dpdk_context(self, mock_create_vm, mock_check_if_exists,
376                                     mock_build_xml, mock_write_file):
377         self.ovs_dpdk.vm_deploy = True
378         self.ovs_dpdk.connection = mock.Mock()
379         self.ovs_dpdk.vm_names = ['vm_0', 'vm_1']
380         self.ovs_dpdk.drivers = []
381         self.ovs_dpdk.servers = {
382             'vnf_0': {
383                 'network_ports': {
384                     'mgmt': {'cidr': '152.16.100.10/24'},
385                     'xe0': ['private_0'],
386                     'xe1': ['public_0']
387                 }
388             }
389         }
390         self.ovs_dpdk.networks = self.NETWORKS
391         self.ovs_dpdk.host_mgmt = {}
392         self.ovs_dpdk.flavor = {}
393         self.ovs_dpdk.configure_nics_for_ovs_dpdk = mock.Mock(return_value="")
394         xml_str = mock.Mock()
395         mock_build_xml.return_value = (xml_str, '00:00:00:00:00:01')
396         self.ovs_dpdk._enable_interfaces = mock.Mock(return_value=xml_str)
397         vnf_instance = mock.Mock()
398         self.ovs_dpdk.vnf_node.generate_vnf_instance = mock.Mock(
399             return_value=vnf_instance)
400
401         self.assertEqual([vnf_instance],
402                          self.ovs_dpdk.setup_ovs_dpdk_context())
403         mock_create_vm.assert_called_once_with(
404             self.ovs_dpdk.connection, '/tmp/vm_ovs_0.xml')
405         mock_check_if_exists.assert_called_once_with(
406             'vm_0', self.ovs_dpdk.connection)
407         mock_build_xml.assert_called_once_with(
408             self.ovs_dpdk.connection, self.ovs_dpdk.vm_flavor, 'vm_0', 0)
409         mock_write_file.assert_called_once_with('/tmp/vm_ovs_0.xml', xml_str)
410
411     @mock.patch.object(io, 'BytesIO')
412     def test__check_hugepages(self, mock_bytesio):
413         data = six.BytesIO('HugePages_Total:      20\n'
414                            'HugePages_Free:       20\n'
415                            'HugePages_Rsvd:        0\n'
416                            'HugePages_Surp:        0\n'
417                            'Hugepagesize:    1048576 kB'.encode())
418         mock_bytesio.return_value = data
419         self.ovs_dpdk.connection = mock.Mock()
420         self.ovs_dpdk._check_hugepages()
421
422     @mock.patch.object(io, 'BytesIO')
423     def test__check_hugepages_no_info(self, mock_bytesio):
424         data = six.BytesIO(''.encode())
425         mock_bytesio.return_value = data
426         self.ovs_dpdk.connection = mock.Mock()
427         with self.assertRaises(exceptions.OVSHugepagesInfoError):
428             self.ovs_dpdk._check_hugepages()
429
430     @mock.patch.object(io, 'BytesIO')
431     def test__check_hugepages_no_total_hp(self, mock_bytesio):
432         data = six.BytesIO('HugePages_Total:       0\n'
433                            'HugePages_Free:        0\n'
434                            'HugePages_Rsvd:        0\n'
435                            'HugePages_Surp:        0\n'
436                            'Hugepagesize:    1048576 kB'.encode())
437         mock_bytesio.return_value = data
438         self.ovs_dpdk.connection = mock.Mock()
439         with self.assertRaises(exceptions.OVSHugepagesNotConfigured):
440             self.ovs_dpdk._check_hugepages()
441
442     @mock.patch.object(io, 'BytesIO')
443     def test__check_hugepages_no_free_hp(self, mock_bytesio):
444         data = six.BytesIO('HugePages_Total:      20\n'
445                            'HugePages_Free:        0\n'
446                            'HugePages_Rsvd:        0\n'
447                            'HugePages_Surp:        0\n'
448                            'Hugepagesize:    1048576 kB'.encode())
449         mock_bytesio.return_value = data
450         self.ovs_dpdk.connection = mock.Mock()
451         with self.assertRaises(exceptions.OVSHugepagesZeroFree) as exc:
452             self.ovs_dpdk._check_hugepages()
453         self.assertEqual('There are no HugePages free in this system. Total '
454                          'HugePages configured: 20', exc.exception.msg)