1 # Copyright 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.
15 """VSPERF VPP implementation using DPDK and vhostuser vports
24 from src.dpdk import dpdk
25 from conf import settings as S
26 from vswitches.vswitch import IVSwitch
27 from tools import tasks
29 # pylint: disable=too-many-public-methods
30 class VppDpdkVhost(IVSwitch, tasks.Process):
31 """ VPP with DPDK support
34 _bridge_idx_counter = 100
37 """See IVswitch for general description
39 self._logfile = os.path.join(S.getValue('LOG_DIR'),
40 S.getValue('LOG_FILE_VPP'))
41 self._logger = logging.getLogger(__name__)
42 self._expect = r'vpp#'
44 self._vswitch_args = []
46 self._cmd_template = ['sudo', '-E', S.getValue('TOOLS')['vpp']]
48 self._logger = logging.getLogger(__name__)
54 tmp_args = copy.deepcopy(S.getValue('VSWITCH_VPP_ARGS'))
55 if 'dpdk' not in tmp_args:
58 # override socket-mem settings
59 for tmp_arg in tmp_args['dpdk']:
60 if tmp_arg.startswith('socket-mem'):
61 tmp_args['dpdk'].remove(tmp_arg)
62 tmp_args['dpdk'].append('socket-mem ' +
63 ','.join(S.getValue('DPDK_SOCKET_MEM')))
65 for nic in S.getValue('NICS'):
66 tmp_args['dpdk'].append("dev {}".format(nic['pci']))
67 self._vswitch_args = self._process_vpp_args(tmp_args)
69 def _get_nic_info(self, key='Name'):
70 """Read NIC info from VPP and return NIC details in a dictionary
71 indexed by given ``key``
73 :param key: Name of the key to be used for indexing result dictionary
75 :returns: Dictionary with NIC infos including their PCI addresses
78 output = self.run_vppctl(['show', 'hardware', 'brief'])
79 # parse output and store basic info about NICS
80 ifaces = output[0].split('\n')
81 keys = ifaces[0].split()
83 keyidx = keys.index(key)
84 for iface in ifaces[1:]:
86 # get PCI address of given interface
87 output = self.run_vppctl(['show', 'hardware', tmpif[1], 'detail'])
88 match = re.search(r'pci address:\s*([\d:\.]+)', output[0])
90 # normalize PCI address, e.g. 0000:05:10.01 => 0000:05:10.1
91 tmp_pci = match.group(1).split('.')
92 tmp_pci[1] = str(int(tmp_pci[1]))
93 tmpif.append('.'.join(tmp_pci))
96 # store only NICs with reasonable index
97 if tmpif[keyidx] is not None:
98 result[tmpif[keyidx]] = dict(zip(keys, tmpif))
102 def _process_vpp_args(self, args):
103 """Produce VPP CLI args from input dictionary ``args``
107 cli_args.append(cfg_key)
108 cli_args.append("{{ {} }}".format(' '.join(args[cfg_key])))
110 self._logger.debug("VPP CLI args: %s", cli_args)
115 """Activates DPDK kernel modules and starts VPP
117 :raises: pexpect.EOF, pexpect.TIMEOUT
120 self._logger.info("Starting VPP...")
122 self._cmd = self._cmd_template + self._vswitch_args
125 tasks.Process.start(self)
127 except (pexpect.EOF, pexpect.TIMEOUT) as exc:
128 logging.error("Exception during VPP start.")
131 self._logger.info("VPP...Started.")
134 """See IVswitch for general description
136 Kills VPP and removes DPDK kernel modules.
138 self._logger.info("Terminating VPP...")
140 self._logger.info("VPP...Terminated.")
143 def kill(self, signal='-15', sleep=10):
144 """See IVswitch for general description
149 output = self.run_vppctl(['show', 'version', 'verbose'])
150 match = re.search(r'Current PID:\s*([0-9]+)', output[0])
152 vpp_pid = match.group(1)
153 tasks.terminate_task(vpp_pid, logger=self._logger)
155 # in case, that pid was not detected or sudo envelope
156 # has not been terminated yet
157 tasks.Process.kill(self, signal, sleep)
159 def add_switch(self, switch_name, dummy_params=None):
160 """See IVswitch for general description
162 if switch_name in self._switches:
163 self._logger.warning("switch %s already exists...", switch_name)
165 self._switches[switch_name] = self._bridge_idx_counter
166 self._bridge_idx_counter += 1
168 def del_switch(self, switch_name):
169 """See IVswitch for general description
171 if switch_name in self._switches:
172 del self._switches[switch_name]
174 def add_phy_port(self, dummy_switch_name):
175 """See IVswitch for general description
176 :raises: RuntimeError
178 # get list of physical interfaces with PCI addresses
179 vpp_nics = self._get_nic_info(key='Pci')
180 # check if there are any NICs left
181 if len(self._phy_ports) >= len(S.getValue('NICS')):
182 raise RuntimeError('All available NICs are already configured!')
184 nic = S.getValue('NICS')[len(self._phy_ports)]
185 if not nic['pci'] in vpp_nics:
186 raise RuntimeError('VPP cannot access nic with PCI address: {}'.format(nic['pci']))
187 nic_name = vpp_nics[nic['pci']]['Name']
188 self._phy_ports.append(nic_name)
189 self.run_vppctl(['set', 'int', 'state', nic_name, 'up'])
190 return (nic_name, vpp_nics[nic['pci']]['Idx'])
192 def add_vport(self, dummy_switch_name):
193 """See IVswitch for general description
195 socket_name = S.getValue('TOOLS')['ovs_var_tmp'] + 'dpdkvhostuser' + str(len(self._virt_ports))
196 output = self.run_vppctl(['create', 'vhost-user', 'socket', socket_name, 'server'] +
197 S.getValue('VSWITCH_VPP_VHOSTUSER_ARGS'))
199 self._virt_ports.append(nic_name)
200 self.run_vppctl(['set', 'int', 'state', nic_name, 'up'])
201 return (nic_name, None)
203 def del_port(self, switch_name, port_name):
204 """See IVswitch for general description
206 if port_name in self._phy_ports:
207 self.run_vppctl(['set', 'int', 'state', port_name, 'down'])
208 self._phy_ports.remove(port_name)
209 elif port_name in self._virt_ports:
210 self.run_vppctl(['set', 'int', 'state', port_name, 'down'])
211 self.run_vppctl(['delete', 'vhost-user', port_name])
212 self._virt_ports.remove(port_name)
214 self._logger.warning("Port %s is not configured.", port_name)
216 def add_l2patch(self, port1, port2, bidir=False):
217 """Create l2patch connection between given ports
219 self.run_vppctl(['test', 'l2patch', 'rx', port1, 'tx', port2])
221 self.run_vppctl(['test', 'l2patch', 'rx', port2, 'tx', port1])
223 def add_xconnect(self, port1, port2, bidir=False):
224 """Create l2patch connection between given ports
226 self.run_vppctl(['set', 'interface', 'l2', 'xconnect', port1, port2])
228 self.run_vppctl(['set', 'interface', 'l2', 'xconnect', port2, port1])
230 def add_bridge(self, switch_name, port1, port2, dummy_bidir=False):
231 """Add given ports to bridge ``switch_name``
233 self.run_vppctl(['set', 'interface', 'l2', 'bridge', port1,
234 str(self._switches[switch_name])])
235 self.run_vppctl(['set', 'interface', 'l2', 'bridge', port2,
236 str(self._switches[switch_name])])
238 def add_connection(self, switch_name, port1, port2, bidir=False):
239 """See IVswitch for general description
241 :raises: RuntimeError
243 mode = S.getValue('VSWITCH_VPP_L2_CONNECT_MODE')
244 if mode == 'l2patch':
245 self.add_l2patch(port1, port2, bidir)
246 elif mode == 'xconnect':
247 self.add_xconnect(port1, port2, bidir)
248 elif mode == 'bridge':
249 self.add_bridge(switch_name, port1, port2)
251 raise RuntimeError('VPP: Unsupported l2 connection mode detected %s', mode)
253 def del_l2patch(self, port1, port2, bidir=False):
254 """Remove l2patch connection between given ports
256 :param port1: port to be used in connection
257 :param port2: port to be used in connection
258 :param bidir: switch between uni and bidirectional traffic
260 self.run_vppctl(['test', 'l2patch', 'rx', port1, 'tx', port2, 'del'])
262 self.run_vppctl(['test', 'l2patch', 'rx', port2, 'tx', port1, 'del'])
264 def del_xconnect(self, dummy_port1, dummy_port2, dummy_bidir=False):
265 """Remove xconnect connection between given ports
267 self._logger.warning('VPP: Removal of l2 xconnect is not implemented.')
269 def del_bridge(self, dummy_switch_name, dummy_port1, dummy_port2):
270 """Remove given ports from the bridge
272 self._logger.warning('VPP: Removal of interfaces from bridge is not implemented.')
274 def del_connection(self, switch_name, port1, port2, bidir=False):
275 """See IVswitch for general description
277 :raises: RuntimeError
279 mode = S.getValue('VSWITCH_VPP_L2_CONNECT_MODE')
280 if mode == 'l2patch':
281 self.del_l2patch(port1, port2, bidir)
282 elif mode == 'xconnect':
283 self.del_xconnect(port1, port2, bidir)
284 elif mode == 'bridge':
285 self.del_bridge(switch_name, port1, port2)
287 raise RuntimeError('VPP: Unsupported l2 connection mode detected %s', mode)
289 def dump_l2patch(self):
290 """Dump l2patch connections
292 self.run_vppctl(['show', 'l2patch'])
294 def dump_xconnect(self):
295 """Dump l2 xconnect connections
297 self._logger.warning("VPP: Dump of l2 xconnections is not supported.")
299 def dump_bridge(self, switch_name):
300 """Show bridge details
302 :param switch_name: switch on which to operate
304 self.run_vppctl(['show', 'bridge-domain', str(self._switches[switch_name]), 'int'])
306 def dump_connections(self, switch_name):
307 """See IVswitch for general description
309 :raises: RuntimeError
311 mode = S.getValue('VSWITCH_VPP_L2_CONNECT_MODE')
312 if mode == 'l2patch':
314 elif mode == 'xconnect':
316 elif mode == 'bridge':
317 self.dump_bridge(switch_name)
319 raise RuntimeError('VPP: Unsupported l2 connection mode detected %s', mode)
321 def run_vppctl(self, args, check_error=False):
322 """Run ``vppctl`` with supplied arguments.
324 :param args: Arguments to pass to ``vppctl``
325 :param check_error: Throw exception on error
329 cmd = ['sudo', S.getValue('TOOLS')['vppctl']] + args
330 return tasks.run_task(cmd, self._logger, 'Running vppctl...', check_error)
335 def validate_add_switch(self, dummy_result, switch_name, dummy_params=None):
336 """Validate - Create a new logical switch with no ports
338 return switch_name in self._switches
340 def validate_del_switch(self, dummy_result, switch_name):
341 """Validate removal of switch
343 return not self.validate_add_switch(dummy_result, switch_name)
345 def validate_add_phy_port(self, result, dummy_switch_name):
346 """ Validate that physical port was added to bridge.
348 return result[0] in self._phy_ports
350 def validate_add_vport(self, result, dummy_switch_name):
351 """ Validate that virtual port was added to bridge.
353 return result[0] in self._virt_ports
355 def validate_del_port(self, dummy_result, dummy_switch_name, port_name):
356 """ Validate that port_name was removed from bridge.
358 return not (port_name in self._phy_ports or port_name in self._virt_ports)
360 # pylint: disable=no-self-use
361 def validate_run_vppctl(self, result, dummy_args, dummy_check_error=False):
362 """validate execution of ``vppctl`` with supplied arguments.
364 # there shouldn't be any stderr
368 # Non implemented methods
370 def add_flow(self, switch_name, flow, cache='off'):
371 """See IVswitch for general description
373 raise NotImplementedError()
375 def del_flow(self, switch_name, flow=None):
376 """See IVswitch for general description
378 raise NotImplementedError()
380 def dump_flows(self, switch_name):
381 """See IVswitch for general description
383 raise NotImplementedError()
385 def add_route(self, switch_name, network, destination):
386 """See IVswitch for general description
388 raise NotImplementedError()
390 def set_tunnel_arp(self, ip_addr, mac_addr, switch_name):
391 """See IVswitch for general description
393 raise NotImplementedError()
395 def add_tunnel_port(self, switch_name, remote_ip, tunnel_type='vxlan', params=None):
396 """See IVswitch for general description
398 raise NotImplementedError()
400 def get_ports(self, switch_name):
401 """See IVswitch for general description
403 raise NotImplementedError()