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