1 # Copyright (c) 2019 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """VPP Configuration File Generator library.
16 TODO: Support initialization with default values,
17 so that we do not need to have block of 6 "Add Unix" commands
18 in 7 various places of CSIT code.
23 from resources.libraries.python.Constants import Constants
24 from resources.libraries.python.ssh import exec_cmd_no_error
25 from resources.libraries.python.topology import NodeType
26 from resources.libraries.python.topology import Topology
27 from resources.libraries.python.VPPUtil import VPPUtil
29 __all__ = ['VppConfigGenerator']
32 def pci_dev_check(pci_dev):
33 """Check if provided PCI address is in correct format.
35 :param pci_dev: PCI address (expected format: xxxx:xx:xx.x).
37 :returns: True if PCI address is in correct format.
39 :raises ValueError: If PCI address is in incorrect format.
41 pattern = re.compile("^[0-9A-Fa-f]{4}:[0-9A-Fa-f]{2}:"
42 "[0-9A-Fa-f]{2}\\.[0-9A-Fa-f]$")
43 if not pattern.match(pci_dev):
44 raise ValueError('PCI address {addr} is not in valid format '
45 'xxxx:xx:xx.x'.format(addr=pci_dev))
49 class VppConfigGenerator(object):
50 """VPP Configuration File Generator."""
53 """Initialize library."""
54 # VPP Node to apply configuration on
60 # Serialized VPP Configuration
63 self._vpp_service_name = 'vpp'
64 # VPP Logfile location
65 self._vpp_logfile = '/tmp/vpe.log'
66 # VPP Startup config location
67 self._vpp_startup_conf = '/etc/vpp/startup.conf'
68 # VPP Startup config backup location
69 self._vpp_startup_conf_backup = None
71 def set_node(self, node):
74 :param node: Node to store configuration on.
76 :raises RuntimeError: If Node type is not DUT.
78 if node['type'] != NodeType.DUT:
79 raise RuntimeError('Startup config can only be applied to DUT'
82 self._hostname = Topology.get_node_hostname(node)
84 def set_vpp_logfile(self, logfile):
85 """Set VPP logfile location.
87 :param logfile: VPP logfile location.
90 self._vpp_logfile = logfile
92 def set_vpp_startup_conf_backup(self, backup='/etc/vpp/startup.backup'):
93 """Set VPP startup configuration backup.
95 :param backup: VPP logfile location.
98 self._vpp_startup_conf_backup = backup
100 def get_config_str(self):
101 """Get dumped startup configuration in VPP config format.
103 :returns: Startup configuration in VPP config format.
106 self.dump_config(self._nodeconfig)
107 return self._vpp_config
109 def add_config_item(self, config, value, path):
110 """Add startup configuration item.
112 :param config: Startup configuration of node.
113 :param value: Value to insert.
114 :param path: Path where to insert item.
120 config[path[0]] = value
122 if path[0] not in config:
124 elif isinstance(config[path[0]], str):
125 config[path[0]] = {} if config[path[0]] == '' \
126 else {config[path[0]]: ''}
127 self.add_config_item(config[path[0]], value, path[1:])
129 def dump_config(self, obj, level=-1):
130 """Dump the startup configuration in VPP config format.
132 :param obj: Python Object to print.
133 :param level: Nested level for indentation.
140 self._vpp_config += '{}{{\n'.format((level) * indent)
141 if isinstance(obj, dict):
142 for key, val in obj.items():
143 if hasattr(val, '__iter__'):
144 self._vpp_config += '{}{}\n'.format((level + 1) * indent,
146 self.dump_config(val, level + 1)
148 self._vpp_config += '{}{} {}\n'.format((level + 1) * indent,
152 self._vpp_config += '{}{}\n'.format((level + 1) * indent, val)
154 self._vpp_config += '{}}}\n'.format(level * indent)
156 def add_unix_log(self, value=None):
157 """Add UNIX log configuration.
159 :param value: Log file.
162 path = ['unix', 'log']
164 value = self._vpp_logfile
165 self.add_config_item(self._nodeconfig, value, path)
167 def add_unix_cli_listen(self, value='/run/vpp/cli.sock'):
168 """Add UNIX cli-listen configuration.
170 :param value: CLI listen address and port or path to CLI socket.
173 path = ['unix', 'cli-listen']
174 self.add_config_item(self._nodeconfig, value, path)
176 def add_unix_gid(self, value='vpp'):
177 """Add UNIX gid configuration.
182 path = ['unix', 'gid']
183 self.add_config_item(self._nodeconfig, value, path)
185 def add_unix_nodaemon(self):
186 """Add UNIX nodaemon configuration."""
187 path = ['unix', 'nodaemon']
188 self.add_config_item(self._nodeconfig, '', path)
190 def add_unix_coredump(self):
191 """Add UNIX full-coredump configuration."""
192 path = ['unix', 'full-coredump']
193 self.add_config_item(self._nodeconfig, '', path)
195 def add_unix_exec(self, value):
196 """Add UNIX exec configuration."""
197 path = ['unix', 'exec']
198 self.add_config_item(self._nodeconfig, value, path)
200 def add_socksvr(self, socket=Constants.SOCKSVR_PATH):
201 """Add socksvr configuration."""
202 path = ['socksvr', 'socket-name']
203 self.add_config_item(self._nodeconfig, socket, path)
205 def add_api_segment_gid(self, value='vpp'):
206 """Add API-SEGMENT gid configuration.
211 path = ['api-segment', 'gid']
212 self.add_config_item(self._nodeconfig, value, path)
214 def add_api_segment_global_size(self, value):
215 """Add API-SEGMENT global-size configuration.
217 :param value: Global size.
220 path = ['api-segment', 'global-size']
221 self.add_config_item(self._nodeconfig, value, path)
223 def add_api_segment_api_size(self, value):
224 """Add API-SEGMENT api-size configuration.
226 :param value: API size.
229 path = ['api-segment', 'api-size']
230 self.add_config_item(self._nodeconfig, value, path)
232 def add_buffers_per_numa(self, value):
233 """Increase number of buffers allocated.
235 :param value: Number of buffers allocated.
238 path = ['buffers', 'buffers-per-numa']
239 self.add_config_item(self._nodeconfig, value, path)
241 def add_dpdk_dev(self, *devices):
242 """Add DPDK PCI device configuration.
244 :param devices: PCI device(s) (format xxxx:xx:xx.x)
247 for device in devices:
248 if pci_dev_check(device):
249 path = ['dpdk', 'dev {0}'.format(device)]
250 self.add_config_item(self._nodeconfig, '', path)
252 def add_dpdk_dev_parameter(self, device, parameter, value):
253 """Add parameter for DPDK device.
255 :param device: PCI device (format xxxx:xx:xx.x).
256 :param parameter: Parameter name.
257 :param value: Parameter value.
262 if pci_dev_check(device):
263 path = ['dpdk', 'dev {0}'.format(device), parameter]
264 self.add_config_item(self._nodeconfig, value, path)
266 def add_dpdk_cryptodev(self, count):
267 """Add DPDK Crypto PCI device configuration.
269 :param count: Number of HW crypto devices to add.
272 cryptodev = Topology.get_cryptodev(self._node)
273 for i in range(count):
274 cryptodev_config = 'dev {0}'.format(
275 re.sub(r'\d.\d$', '1.'+str(i), cryptodev))
276 path = ['dpdk', cryptodev_config]
277 self.add_config_item(self._nodeconfig, '', path)
278 self.add_dpdk_uio_driver('vfio-pci')
280 def add_dpdk_sw_cryptodev(self, sw_pmd_type, socket_id, count):
281 """Add DPDK SW Crypto device configuration.
283 :param sw_pmd_type: Type of SW crypto device PMD to add.
284 :param socket_id: Socket ID.
285 :param count: Number of SW crypto devices to add.
286 :type sw_pmd_type: str
290 for _ in range(count):
291 cryptodev_config = 'vdev cryptodev_{0}_pmd,socket_id={1}'.\
292 format(sw_pmd_type, str(socket_id))
293 path = ['dpdk', cryptodev_config]
294 self.add_config_item(self._nodeconfig, '', path)
296 def add_dpdk_eth_bond_dev(self, ethbond_id, mode, xmit_policy, *slaves):
297 """Add DPDK Eth_bond device configuration.
299 :param ethbond_id: Eth_bond device ID.
300 :param mode: Link bonding mode.
301 :param xmit_policy: Transmission policy.
302 :param slaves: PCI device(s) to be bonded (format xxxx:xx:xx.x).
303 :type ethbond_id: str or int
304 :type mode: str or int
305 :type xmit_policy: str
308 slaves_config = ',slave=' + \
309 ',slave='.join(slave if pci_dev_check(slave) else ''
311 ethbond_config = 'vdev eth_bond{id},mode={mode}{slaves},' \
312 'xmit_policy={xmit_pol}'.format(id=ethbond_id,
314 slaves=slaves_config,
315 xmit_pol=xmit_policy)
316 path = ['dpdk', ethbond_config]
317 self.add_config_item(self._nodeconfig, '', path)
319 def add_dpdk_dev_default_rxq(self, value):
320 """Add DPDK dev default rxq configuration.
322 :param value: Default number of rxqs.
325 path = ['dpdk', 'dev default', 'num-rx-queues']
326 self.add_config_item(self._nodeconfig, value, path)
328 def add_dpdk_dev_default_txq(self, value):
329 """Add DPDK dev default txq configuration.
331 :param value: Default number of txqs.
334 path = ['dpdk', 'dev default', 'num-tx-queues']
335 self.add_config_item(self._nodeconfig, value, path)
337 def add_dpdk_dev_default_rxd(self, value):
338 """Add DPDK dev default rxd configuration.
340 :param value: Default number of rxds.
343 path = ['dpdk', 'dev default', 'num-rx-desc']
344 self.add_config_item(self._nodeconfig, value, path)
346 def add_dpdk_dev_default_txd(self, value):
347 """Add DPDK dev default txd configuration.
349 :param value: Default number of txds.
352 path = ['dpdk', 'dev default', 'num-tx-desc']
353 self.add_config_item(self._nodeconfig, value, path)
355 def add_dpdk_log_level(self, value):
356 """Add DPDK log-level configuration.
358 :param value: Log level.
361 path = ['dpdk', 'log-level']
362 self.add_config_item(self._nodeconfig, value, path)
364 def add_dpdk_no_pci(self):
365 """Add DPDK no-pci."""
366 path = ['dpdk', 'no-pci']
367 self.add_config_item(self._nodeconfig, '', path)
369 def add_dpdk_uio_driver(self, value=None):
370 """Add DPDK uio-driver configuration.
372 :param value: DPDK uio-driver configuration. By default, driver will be
373 loaded automatically from Topology file, still leaving
374 option to manually override by parameter.
378 value = Topology.get_uio_driver(self._node)
379 path = ['dpdk', 'uio-driver']
380 self.add_config_item(self._nodeconfig, value, path)
382 def add_cpu_main_core(self, value):
383 """Add CPU main core configuration.
385 :param value: Main core option.
388 path = ['cpu', 'main-core']
389 self.add_config_item(self._nodeconfig, value, path)
391 def add_cpu_corelist_workers(self, value):
392 """Add CPU corelist-workers configuration.
394 :param value: Corelist-workers option.
397 path = ['cpu', 'corelist-workers']
398 self.add_config_item(self._nodeconfig, value, path)
400 def add_heapsize(self, value):
401 """Add Heapsize configuration.
403 :param value: Amount of heapsize.
407 self.add_config_item(self._nodeconfig, value, path)
409 def add_api_trace(self):
410 """Add API trace configuration."""
411 path = ['api-trace', 'on']
412 self.add_config_item(self._nodeconfig, '', path)
414 def add_ip6_hash_buckets(self, value):
415 """Add IP6 hash buckets configuration.
417 :param value: Number of IP6 hash buckets.
420 path = ['ip6', 'hash-buckets']
421 self.add_config_item(self._nodeconfig, value, path)
423 def add_ip6_heap_size(self, value):
424 """Add IP6 heap-size configuration.
426 :param value: IP6 Heapsize amount.
429 path = ['ip6', 'heap-size']
430 self.add_config_item(self._nodeconfig, value, path)
432 def add_ip_heap_size(self, value):
433 """Add IP heap-size configuration.
435 :param value: IP Heapsize amount.
438 path = ['ip', 'heap-size']
439 self.add_config_item(self._nodeconfig, value, path)
441 def add_statseg_size(self, value):
442 """Add stats segment heap size configuration.
444 :param value: Stats heapsize amount.
447 path = ['statseg', 'size']
448 self.add_config_item(self._nodeconfig, value, path)
450 def add_statseg_per_node_counters(self, value):
451 """Add stats per-node-counters configuration.
453 :param value: "on" to switch the counters on.
456 path = ['statseg', 'per-node-counters']
457 self.add_config_item(self._nodeconfig, value, path)
459 def add_plugin(self, state, *plugins):
460 """Add plugin section for specific plugin(s).
462 :param state: State of plugin [enable|disable].
463 :param plugins: Plugin(s) to disable.
467 for plugin in plugins:
468 path = ['plugins', 'plugin {0}'.format(plugin), state]
469 self.add_config_item(self._nodeconfig, ' ', path)
471 def add_dpdk_no_multi_seg(self):
472 """Add DPDK no-multi-seg configuration."""
473 path = ['dpdk', 'no-multi-seg']
474 self.add_config_item(self._nodeconfig, '', path)
476 def add_dpdk_no_tx_checksum_offload(self):
477 """Add DPDK no-tx-checksum-offload configuration."""
478 path = ['dpdk', 'no-tx-checksum-offload']
479 self.add_config_item(self._nodeconfig, '', path)
481 def add_nat(self, value='deterministic'):
482 """Add NAT configuration.
484 :param value: NAT mode.
488 self.add_config_item(self._nodeconfig, value, path)
490 def add_tcp_preallocated_connections(self, value):
491 """Add TCP pre-allocated connections.
493 :param value: The number of pre-allocated connections.
496 path = ['tcp', 'preallocated-connections']
497 self.add_config_item(self._nodeconfig, value, path)
499 def add_tcp_preallocated_half_open_connections(self, value):
500 """Add TCP pre-allocated half open connections.
502 :param value: The number of pre-allocated half open connections.
505 path = ['tcp', 'preallocated-half-open-connections']
506 self.add_config_item(self._nodeconfig, value, path)
508 def add_session_event_queue_length(self, value):
509 """Add session event queue length.
511 :param value: Session event queue length.
514 path = ['session', 'event-queue-length']
515 self.add_config_item(self._nodeconfig, value, path)
517 def add_session_preallocated_sessions(self, value):
518 """Add the number of pre-allocated sessions.
520 :param value: Number of pre-allocated sessions.
523 path = ['session', 'preallocated-sessions']
524 self.add_config_item(self._nodeconfig, value, path)
526 def add_session_v4_session_table_buckets(self, value):
527 """Add number of v4 session table buckets to the config.
529 :param value: Number of v4 session table buckets.
532 path = ['session', 'v4-session-table-buckets']
533 self.add_config_item(self._nodeconfig, value, path)
535 def add_session_v4_session_table_memory(self, value):
536 """Add the size of v4 session table memory.
538 :param value: Size of v4 session table memory.
541 path = ['session', 'v4-session-table-memory']
542 self.add_config_item(self._nodeconfig, value, path)
544 def add_session_v4_halfopen_table_buckets(self, value):
545 """Add the number of v4 halfopen table buckets.
547 :param value: Number of v4 halfopen table buckets.
550 path = ['session', 'v4-halfopen-table-buckets']
551 self.add_config_item(self._nodeconfig, value, path)
553 def add_session_v4_halfopen_table_memory(self, value):
554 """Add the size of v4 halfopen table memory.
556 :param value: Size of v4 halfopen table memory.
559 path = ['session', 'v4-halfopen-table-memory']
560 self.add_config_item(self._nodeconfig, value, path)
562 def add_session_local_endpoints_table_buckets(self, value):
563 """Add the number of local endpoints table buckets.
565 :param value: Number of local endpoints table buckets.
568 path = ['session', 'local-endpoints-table-buckets']
569 self.add_config_item(self._nodeconfig, value, path)
571 def add_session_local_endpoints_table_memory(self, value):
572 """Add the size of local endpoints table memory.
574 :param value: Size of local endpoints table memory.
577 path = ['session', 'local-endpoints-table-memory']
578 self.add_config_item(self._nodeconfig, value, path)
580 def write_config(self, filename=None):
581 """Generate and write VPP startup configuration to file.
583 Use data from calls to this class to form a startup.conf file and
584 replace /etc/vpp/startup.conf with it on topology node.
586 :param filename: Startup configuration file name.
589 self.dump_config(self._nodeconfig)
592 filename = self._vpp_startup_conf
594 if self._vpp_startup_conf_backup is not None:
595 cmd = ('cp {src} {dest}'.format(
596 src=self._vpp_startup_conf, dest=self._vpp_startup_conf_backup))
598 self._node, cmd, sudo=True, message='Copy config file failed!')
600 cmd = ('echo "{config}" | sudo tee {filename}'.format(
601 config=self._vpp_config, filename=filename))
603 self._node, cmd, message='Writing config file failed!')
605 def apply_config(self, filename=None, verify_vpp=True):
606 """Generate and write VPP startup configuration to file and restart VPP.
608 Use data from calls to this class to form a startup.conf file and
609 replace /etc/vpp/startup.conf with it on topology node.
611 :param filename: Startup configuration file name.
612 :param verify_vpp: Verify VPP is running after restart.
614 :type verify_vpp: bool
616 self.write_config(filename=filename)
618 VPPUtil.restart_vpp_service(self._node)
620 VPPUtil.verify_vpp(self._node)
622 def restore_config(self):
623 """Restore VPP startup.conf from backup."""
624 cmd = ('cp {src} {dest}'.format(
625 src=self._vpp_startup_conf_backup, dest=self._vpp_startup_conf))
627 self._node, cmd, sudo=True, message='Copy config file failed!')