1 # Copyright (c) 2016-2017 Intel Corporation
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
21 NETWORK_KERNEL = 'network_kernel'
22 NETWORK_DPDK = 'network_dpdk'
23 NETWORK_OTHER = 'network_other'
24 CRYPTO_KERNEL = 'crypto_kernel'
25 CRYPTO_DPDK = 'crypto_dpdk'
26 CRYPTO_OTHER = 'crypto_other'
29 LOG = logging.getLogger(__name__)
32 class DpdkBindHelperException(Exception):
36 class DpdkBindHelper(object):
37 DPDK_STATUS_CMD = "{dpdk_devbind} --status"
38 DPDK_BIND_CMD = "sudo {dpdk_devbind} {force} -b {driver} {vpci}"
40 NIC_ROW_RE = re.compile(r"([^ ]+) '([^']+)' (?:if=([^ ]+) )?drv=([^ ]+) "
41 r"unused=([^ ]*)(?: (\*Active\*))?")
42 SKIP_RE = re.compile('(====|<none>|^$)')
43 NIC_ROW_FIELDS = ['vpci', 'dev_type', 'iface', 'driver', 'unused', 'active']
46 (re.compile('^Network.*DPDK.*$'), NETWORK_DPDK),
47 (re.compile('^Network.*kernel.*$'), NETWORK_KERNEL),
48 (re.compile('^Other network.*$'), NETWORK_OTHER),
49 (re.compile('^Crypto.*DPDK.*$'), CRYPTO_DPDK),
50 (re.compile('^Crypto.*kernel$'), CRYPTO_KERNEL),
51 (re.compile('^Other crypto.*$'), CRYPTO_OTHER),
54 def clean_status(self):
64 def __init__(self, ssh_helper):
65 self.dpdk_status = None
66 self.status_nic_row_re = None
67 self._dpdk_devbind = None
68 self._status_cmd_attr = None
70 self.ssh_helper = ssh_helper
73 def _dpdk_execute(self, *args, **kwargs):
74 res = self.ssh_helper.execute(*args, **kwargs)
76 raise DpdkBindHelperException('{} command failed with rc={}'.format(
77 self.dpdk_devbind, res[0]))
81 def dpdk_devbind(self):
82 if self._dpdk_devbind is None:
83 self._dpdk_devbind = self.ssh_helper.provision_tool(tool_file="dpdk-devbind.py")
84 return self._dpdk_devbind
87 def _status_cmd(self):
88 if self._status_cmd_attr is None:
89 self._status_cmd_attr = self.DPDK_STATUS_CMD.format(dpdk_devbind=self.dpdk_devbind)
90 return self._status_cmd_attr
92 def _addline(self, active_list, line):
93 if active_list is None:
95 res = self.NIC_ROW_RE.match(line)
98 new_data = {k: v for k, v in zip(self.NIC_ROW_FIELDS, res.groups())}
99 new_data['active'] = bool(new_data['active'])
100 self.dpdk_status[active_list].append(new_data)
103 def _switch_active_dict(cls, a_row, active_dict):
104 for regexp, a_dict in cls.HEADER_DICT_PAIRS:
105 if regexp.match(a_row):
109 def parse_dpdk_status_output(self, input):
112 for a_row in input.splitlines():
113 if self.SKIP_RE.match(a_row):
115 active_dict = self._switch_active_dict(a_row, active_dict)
116 self._addline(active_dict, a_row)
117 return self.dpdk_status
119 def _get_bound_pci_addresses(self, active_dict):
120 return [iface['vpci'] for iface in self.dpdk_status[active_dict]]
123 def dpdk_bound_pci_addresses(self):
124 return self._get_bound_pci_addresses(NETWORK_DPDK)
127 def kernel_bound_pci_addresses(self):
128 return self._get_bound_pci_addresses(NETWORK_KERNEL)
131 def interface_driver_map(self):
132 return {interface['vpci']: interface['driver']
133 for interface in itertools.chain.from_iterable(self.dpdk_status.values())}
135 def read_status(self):
136 return self.parse_dpdk_status_output(self._dpdk_execute(self._status_cmd)[1])
138 def bind(self, pci_addresses, driver, force=True):
139 # accept single PCI or list of PCI
140 if isinstance(pci_addresses, six.string_types):
141 pci_addresses = [pci_addresses]
142 cmd = self.DPDK_BIND_CMD.format(dpdk_devbind=self.dpdk_devbind,
144 vpci=' '.join(list(pci_addresses)),
145 force='--force' if force else '')
147 self._dpdk_execute(cmd)
148 # update the inner status dict
151 def save_used_drivers(self):
152 # invert the map, so we can bind by driver type
153 self.used_drivers = {}
154 # sort for stabililty
155 for vpci, driver in sorted(self.interface_driver_map.items()):
156 self.used_drivers.setdefault(driver, []).append(vpci)
158 def rebind_drivers(self, force=True):
159 for driver, vpcis in self.used_drivers.items():
160 self.bind(vpcis, driver, force)