Merge "prox: use find_relative_file when uploading"
[yardstick.git] / yardstick / network_services / helpers / dpdknicbind_helper.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 import re
15 import itertools
16
17 NETWORK_KERNEL = 'network_kernel'
18 NETWORK_DPDK = 'network_dpdk'
19 NETWORK_OTHER = 'network_other'
20 CRYPTO_KERNEL = 'crypto_kernel'
21 CRYPTO_DPDK = 'crypto_dpdk'
22 CRYPTO_OTHER = 'crypto_other'
23
24
25 class DpdkBindHelperException(Exception):
26     pass
27
28
29 class DpdkBindHelper(object):
30     DPDK_STATUS_CMD = "{dpdk_nic_bind} --status"
31     DPDK_BIND_CMD = "sudo {dpdk_nic_bind} {force} -b {driver} {vpci}"
32
33     NIC_ROW_RE = re.compile("([^ ]+) '([^']+)' (?:if=([^ ]+) )?drv=([^ ]+) "
34                             "unused=([^ ]*)(?: (\*Active\*))?")
35     SKIP_RE = re.compile('(====|<none>|^$)')
36     NIC_ROW_FIELDS = ['vpci', 'dev_type', 'iface', 'driver', 'unused', 'active']
37
38     HEADER_DICT_PAIRS = [
39         (re.compile('^Network.*DPDK.*$'), NETWORK_DPDK),
40         (re.compile('^Network.*kernel.*$'), NETWORK_KERNEL),
41         (re.compile('^Other network.*$'), NETWORK_OTHER),
42         (re.compile('^Crypto.*DPDK.*$'), CRYPTO_DPDK),
43         (re.compile('^Crypto.*kernel$'), CRYPTO_KERNEL),
44         (re.compile('^Other crypto.*$'), CRYPTO_OTHER),
45     ]
46
47     def clean_status(self):
48         self.dpdk_status = {
49             NETWORK_KERNEL: [],
50             NETWORK_DPDK: [],
51             CRYPTO_KERNEL: [],
52             CRYPTO_DPDK: [],
53             NETWORK_OTHER: [],
54             CRYPTO_OTHER: [],
55         }
56
57     def __init__(self, ssh_helper):
58         self.dpdk_status = None
59         self.status_nic_row_re = None
60         self._dpdk_nic_bind_attr = None
61         self._status_cmd_attr = None
62
63         self.ssh_helper = ssh_helper
64         self.clean_status()
65
66     def _dpdk_execute(self, *args, **kwargs):
67         res = self.ssh_helper.execute(*args, **kwargs)
68         if res[0] != 0:
69             raise DpdkBindHelperException('{} command failed with rc={}'.format(
70                 self._dpdk_nic_bind, res[0]))
71         return res
72
73     @property
74     def _dpdk_nic_bind(self):
75         if self._dpdk_nic_bind_attr is None:
76             self._dpdk_nic_bind_attr = self.ssh_helper.provision_tool(tool_file="dpdk-devbind.py")
77         return self._dpdk_nic_bind_attr
78
79     @property
80     def _status_cmd(self):
81         if self._status_cmd_attr is None:
82             self._status_cmd_attr = self.DPDK_STATUS_CMD.format(dpdk_nic_bind=self._dpdk_nic_bind)
83         return self._status_cmd_attr
84
85     def _addline(self, active_list, line):
86         if active_list is None:
87             return
88         res = self.NIC_ROW_RE.match(line)
89         if res is None:
90             return
91         new_data = {k: v for k, v in zip(self.NIC_ROW_FIELDS, res.groups())}
92         new_data['active'] = bool(new_data['active'])
93         self.dpdk_status[active_list].append(new_data)
94
95     @classmethod
96     def _switch_active_dict(cls, a_row, active_dict):
97         for regexp, a_dict in cls.HEADER_DICT_PAIRS:
98             if regexp.match(a_row):
99                 return a_dict
100         return active_dict
101
102     def parse_dpdk_status_output(self, input):
103         active_dict = None
104         self.clean_status()
105         for a_row in input.splitlines():
106             if self.SKIP_RE.match(a_row):
107                 continue
108             active_dict = self._switch_active_dict(a_row, active_dict)
109             self._addline(active_dict, a_row)
110         return self.dpdk_status
111
112     def _get_bound_pci_addresses(self, active_dict):
113         return [iface['vpci'] for iface in self.dpdk_status[active_dict]]
114
115     @property
116     def dpdk_bound_pci_addresses(self):
117         return self._get_bound_pci_addresses(NETWORK_DPDK)
118
119     @property
120     def kernel_bound_pci_addresses(self):
121         return self._get_bound_pci_addresses(NETWORK_KERNEL)
122
123     @property
124     def interface_driver_map(self):
125         return {interface['vpci']: interface['driver']
126                 for interface in itertools.chain(*self.dpdk_status.values())}
127
128     def read_status(self):
129         return self.parse_dpdk_status_output(self._dpdk_execute(self._status_cmd)[1])
130
131     def bind(self, pci, driver, force=True):
132         cmd = self.DPDK_BIND_CMD.format(dpdk_nic_bind=self._dpdk_nic_bind,
133                                         driver=driver,
134                                         vpci=' '.join(list(pci)),
135                                         force='--force' if force else '')
136         self._dpdk_execute(cmd)
137         # update the inner status dict
138         self.read_status()
139
140     def save_used_drivers(self):
141         self.used_drivers = self.interface_driver_map
142
143     def rebind_drivers(self, force=True):
144         for vpci, driver in self.used_drivers.items():
145             self.bind(vpci, driver, force)