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."""
18 from resources.libraries.python.ssh import exec_cmd_no_error
19 from resources.libraries.python.topology import NodeType
20 from resources.libraries.python.topology import Topology
21 from resources.libraries.python.VPPUtil import VPPUtil
23 __all__ = ['VppConfigGenerator']
26 def pci_dev_check(pci_dev):
27 """Check if provided PCI address is in correct format.
29 :param pci_dev: PCI address (expected format: xxxx:xx:xx.x).
31 :returns: True if PCI address is in correct format.
33 :raises ValueError: If PCI address is in incorrect format.
35 pattern = re.compile("^[0-9A-Fa-f]{4}:[0-9A-Fa-f]{2}:"
36 "[0-9A-Fa-f]{2}\\.[0-9A-Fa-f]$")
37 if not pattern.match(pci_dev):
38 raise ValueError('PCI address {addr} is not in valid format '
39 'xxxx:xx:xx.x'.format(addr=pci_dev))
43 class VppConfigGenerator(object):
44 """VPP Configuration File Generator."""
47 """Initialize library."""
48 # VPP Node to apply configuration on
54 # Serialized VPP Configuration
57 self._vpp_service_name = 'vpp'
58 # VPP Logfile location
59 self._vpp_logfile = '/tmp/vpe.log'
60 # VPP Startup config location
61 self._vpp_startup_conf = '/etc/vpp/startup.conf'
62 # VPP Startup config backup location
63 self._vpp_startup_conf_backup = None
65 def set_node(self, node):
68 :param node: Node to store configuration on.
70 :raises RuntimeError: If Node type is not DUT.
72 if node['type'] != NodeType.DUT:
73 raise RuntimeError('Startup config can only be applied to DUT'
76 self._hostname = Topology.get_node_hostname(node)
78 def set_vpp_logfile(self, logfile):
79 """Set VPP logfile location.
81 :param logfile: VPP logfile location.
84 self._vpp_logfile = logfile
86 def set_vpp_startup_conf_backup(self, backup='/etc/vpp/startup.backup'):
87 """Set VPP startup configuration backup.
89 :param backup: VPP logfile location.
92 self._vpp_startup_conf_backup = backup
94 def get_config_str(self):
95 """Get dumped startup configuration in VPP config format.
97 :returns: Startup configuration in VPP config format.
100 self.dump_config(self._nodeconfig)
101 return self._vpp_config
103 def add_config_item(self, config, value, path):
104 """Add startup configuration item.
106 :param config: Startup configuration of node.
107 :param value: Value to insert.
108 :param path: Path where to insert item.
114 config[path[0]] = value
116 if path[0] not in config:
118 elif isinstance(config[path[0]], str):
119 config[path[0]] = {} if config[path[0]] == '' \
120 else {config[path[0]]: ''}
121 self.add_config_item(config[path[0]], value, path[1:])
123 def dump_config(self, obj, level=-1):
124 """Dump the startup configuration in VPP config format.
126 :param obj: Python Object to print.
127 :param level: Nested level for indentation.
134 self._vpp_config += '{}{{\n'.format((level) * indent)
135 if isinstance(obj, dict):
136 for key, val in obj.items():
137 if hasattr(val, '__iter__'):
138 self._vpp_config += '{}{}\n'.format((level + 1) * indent,
140 self.dump_config(val, level + 1)
142 self._vpp_config += '{}{} {}\n'.format((level + 1) * indent,
146 self._vpp_config += '{}{}\n'.format((level + 1) * indent, val)
148 self._vpp_config += '{}}}\n'.format(level * indent)
150 def add_unix_log(self, value=None):
151 """Add UNIX log configuration.
153 :param value: Log file.
156 path = ['unix', 'log']
158 value = self._vpp_logfile
159 self.add_config_item(self._nodeconfig, value, path)
161 def add_unix_cli_listen(self, value='/run/vpp/cli.sock'):
162 """Add UNIX cli-listen configuration.
164 :param value: CLI listen address and port or path to CLI socket.
167 path = ['unix', 'cli-listen']
168 self.add_config_item(self._nodeconfig, value, path)
170 def add_unix_gid(self, value='vpp'):
171 """Add UNIX gid configuration.
176 path = ['unix', 'gid']
177 self.add_config_item(self._nodeconfig, value, path)
179 def add_unix_nodaemon(self):
180 """Add UNIX nodaemon configuration."""
181 path = ['unix', 'nodaemon']
182 self.add_config_item(self._nodeconfig, '', path)
184 def add_unix_coredump(self):
185 """Add UNIX full-coredump configuration."""
186 path = ['unix', 'full-coredump']
187 self.add_config_item(self._nodeconfig, '', path)
189 def add_unix_exec(self, value):
190 """Add UNIX exec configuration."""
191 path = ['unix', 'exec']
192 self.add_config_item(self._nodeconfig, value, path)
194 def add_api_segment_gid(self, value='vpp'):
195 """Add API-SEGMENT gid configuration.
200 path = ['api-segment', 'gid']
201 self.add_config_item(self._nodeconfig, value, path)
203 def add_api_segment_global_size(self, value):
204 """Add API-SEGMENT global-size configuration.
206 :param value: Global size.
209 path = ['api-segment', 'global-size']
210 self.add_config_item(self._nodeconfig, value, path)
212 def add_api_segment_api_size(self, value):
213 """Add API-SEGMENT api-size configuration.
215 :param value: API size.
218 path = ['api-segment', 'api-size']
219 self.add_config_item(self._nodeconfig, value, path)
221 def add_buffers_per_numa(self, value):
222 """Increase number of buffers allocated.
224 :param value: Number of buffers allocated.
227 path = ['buffers', 'buffers-per-numa']
228 self.add_config_item(self._nodeconfig, value, path)
230 def add_dpdk_dev(self, *devices):
231 """Add DPDK PCI device configuration.
233 :param devices: PCI device(s) (format xxxx:xx:xx.x)
236 for device in devices:
237 if pci_dev_check(device):
238 path = ['dpdk', 'dev {0}'.format(device)]
239 self.add_config_item(self._nodeconfig, '', path)
241 def add_dpdk_dev_parameter(self, device, parameter, value):
242 """Add parameter for DPDK device.
244 :param device: PCI device (format xxxx:xx:xx.x).
245 :param parameter: Parameter name.
246 :param value: Parameter value.
251 if pci_dev_check(device):
252 path = ['dpdk', 'dev {0}'.format(device), parameter]
253 self.add_config_item(self._nodeconfig, value, path)
255 def add_dpdk_cryptodev(self, count):
256 """Add DPDK Crypto PCI device configuration.
258 :param count: Number of HW crypto devices to add.
261 cryptodev = Topology.get_cryptodev(self._node)
262 for i in range(count):
263 cryptodev_config = 'dev {0}'.format(
264 re.sub(r'\d.\d$', '1.'+str(i), cryptodev))
265 path = ['dpdk', cryptodev_config]
266 self.add_config_item(self._nodeconfig, '', path)
267 self.add_dpdk_uio_driver('vfio-pci')
269 def add_dpdk_sw_cryptodev(self, sw_pmd_type, socket_id, count):
270 """Add DPDK SW Crypto device configuration.
272 :param sw_pmd_type: Type of SW crypto device PMD to add.
273 :param socket_id: Socket ID.
274 :param count: Number of SW crypto devices to add.
275 :type sw_pmd_type: str
279 for _ in range(count):
280 cryptodev_config = 'vdev cryptodev_{0}_pmd,socket_id={1}'.\
281 format(sw_pmd_type, str(socket_id))
282 path = ['dpdk', cryptodev_config]
283 self.add_config_item(self._nodeconfig, '', path)
285 def add_dpdk_eth_bond_dev(self, ethbond_id, mode, xmit_policy, *slaves):
286 """Add DPDK Eth_bond device configuration.
288 :param ethbond_id: Eth_bond device ID.
289 :param mode: Link bonding mode.
290 :param xmit_policy: Transmission policy.
291 :param slaves: PCI device(s) to be bonded (format xxxx:xx:xx.x).
292 :type ethbond_id: str or int
293 :type mode: str or int
294 :type xmit_policy: str
297 slaves_config = ',slave=' + \
298 ',slave='.join(slave if pci_dev_check(slave) else ''
300 ethbond_config = 'vdev eth_bond{id},mode={mode}{slaves},' \
301 'xmit_policy={xmit_pol}'.format(id=ethbond_id,
303 slaves=slaves_config,
304 xmit_pol=xmit_policy)
305 path = ['dpdk', ethbond_config]
306 self.add_config_item(self._nodeconfig, '', path)
308 def add_dpdk_dev_default_rxq(self, value):
309 """Add DPDK dev default rxq configuration.
311 :param value: Default number of rxqs.
314 path = ['dpdk', 'dev default', 'num-rx-queues']
315 self.add_config_item(self._nodeconfig, value, path)
317 def add_dpdk_dev_default_txq(self, value):
318 """Add DPDK dev default txq configuration.
320 :param value: Default number of txqs.
323 path = ['dpdk', 'dev default', 'num-tx-queues']
324 self.add_config_item(self._nodeconfig, value, path)
326 def add_dpdk_dev_default_rxd(self, value):
327 """Add DPDK dev default rxd configuration.
329 :param value: Default number of rxds.
332 path = ['dpdk', 'dev default', 'num-rx-desc']
333 self.add_config_item(self._nodeconfig, value, path)
335 def add_dpdk_dev_default_txd(self, value):
336 """Add DPDK dev default txd configuration.
338 :param value: Default number of txds.
341 path = ['dpdk', 'dev default', 'num-tx-desc']
342 self.add_config_item(self._nodeconfig, value, path)
344 def add_dpdk_log_level(self, value):
345 """Add DPDK log-level configuration.
347 :param value: Log level.
350 path = ['dpdk', 'log-level']
351 self.add_config_item(self._nodeconfig, value, path)
353 def add_dpdk_no_pci(self):
354 """Add DPDK no-pci."""
355 path = ['dpdk', 'no-pci']
356 self.add_config_item(self._nodeconfig, '', path)
358 def add_dpdk_uio_driver(self, value=None):
359 """Add DPDK uio-driver configuration.
361 :param value: DPDK uio-driver configuration. By default, driver will be
362 loaded automatically from Topology file, still leaving
363 option to manually override by parameter.
367 value = Topology.get_uio_driver(self._node)
368 path = ['dpdk', 'uio-driver']
369 self.add_config_item(self._nodeconfig, value, path)
371 def add_cpu_main_core(self, value):
372 """Add CPU main core configuration.
374 :param value: Main core option.
377 path = ['cpu', 'main-core']
378 self.add_config_item(self._nodeconfig, value, path)
380 def add_cpu_corelist_workers(self, value):
381 """Add CPU corelist-workers configuration.
383 :param value: Corelist-workers option.
386 path = ['cpu', 'corelist-workers']
387 self.add_config_item(self._nodeconfig, value, path)
389 def add_heapsize(self, value):
390 """Add Heapsize configuration.
392 :param value: Amount of heapsize.
396 self.add_config_item(self._nodeconfig, value, path)
398 def add_api_trace(self):
399 """Add API trace configuration."""
400 path = ['api-trace', 'on']
401 self.add_config_item(self._nodeconfig, '', path)
403 def add_ip6_hash_buckets(self, value):
404 """Add IP6 hash buckets configuration.
406 :param value: Number of IP6 hash buckets.
409 path = ['ip6', 'hash-buckets']
410 self.add_config_item(self._nodeconfig, value, path)
412 def add_ip6_heap_size(self, value):
413 """Add IP6 heap-size configuration.
415 :param value: IP6 Heapsize amount.
418 path = ['ip6', 'heap-size']
419 self.add_config_item(self._nodeconfig, value, path)
421 def add_ip_heap_size(self, value):
422 """Add IP heap-size configuration.
424 :param value: IP Heapsize amount.
427 path = ['ip', 'heap-size']
428 self.add_config_item(self._nodeconfig, value, path)
430 def add_statseg_size(self, value):
431 """Add stats segment heap size configuration.
433 :param value: Stats heapsize amount.
436 path = ['statseg', 'size']
437 self.add_config_item(self._nodeconfig, value, path)
439 def add_plugin(self, state, *plugins):
440 """Add plugin section for specific plugin(s).
442 :param state: State of plugin [enable|disable].
443 :param plugins: Plugin(s) to disable.
447 for plugin in plugins:
448 path = ['plugins', 'plugin {0}'.format(plugin), state]
449 self.add_config_item(self._nodeconfig, ' ', path)
451 def add_dpdk_no_multi_seg(self):
452 """Add DPDK no-multi-seg configuration."""
453 path = ['dpdk', 'no-multi-seg']
454 self.add_config_item(self._nodeconfig, '', path)
456 def add_dpdk_no_tx_checksum_offload(self):
457 """Add DPDK no-tx-checksum-offload configuration."""
458 path = ['dpdk', 'no-tx-checksum-offload']
459 self.add_config_item(self._nodeconfig, '', path)
461 def add_nat(self, value='deterministic'):
462 """Add NAT configuration.
464 :param value: NAT mode.
468 self.add_config_item(self._nodeconfig, value, path)
470 def add_tcp_preallocated_connections(self, value):
471 """Add TCP pre-allocated connections.
473 :param value: The number of pre-allocated connections.
476 path = ['tcp', 'preallocated-connections']
477 self.add_config_item(self._nodeconfig, value, path)
479 def add_tcp_preallocated_half_open_connections(self, value):
480 """Add TCP pre-allocated half open connections.
482 :param value: The number of pre-allocated half open connections.
485 path = ['tcp', 'preallocated-half-open-connections']
486 self.add_config_item(self._nodeconfig, value, path)
488 def add_session_event_queue_length(self, value):
489 """Add session event queue length.
491 :param value: Session event queue length.
494 path = ['session', 'event-queue-length']
495 self.add_config_item(self._nodeconfig, value, path)
497 def add_session_preallocated_sessions(self, value):
498 """Add the number of pre-allocated sessions.
500 :param value: Number of pre-allocated sessions.
503 path = ['session', 'preallocated-sessions']
504 self.add_config_item(self._nodeconfig, value, path)
506 def add_session_v4_session_table_buckets(self, value):
507 """Add number of v4 session table buckets to the config.
509 :param value: Number of v4 session table buckets.
512 path = ['session', 'v4-session-table-buckets']
513 self.add_config_item(self._nodeconfig, value, path)
515 def add_session_v4_session_table_memory(self, value):
516 """Add the size of v4 session table memory.
518 :param value: Size of v4 session table memory.
521 path = ['session', 'v4-session-table-memory']
522 self.add_config_item(self._nodeconfig, value, path)
524 def add_session_v4_halfopen_table_buckets(self, value):
525 """Add the number of v4 halfopen table buckets.
527 :param value: Number of v4 halfopen table buckets.
530 path = ['session', 'v4-halfopen-table-buckets']
531 self.add_config_item(self._nodeconfig, value, path)
533 def add_session_v4_halfopen_table_memory(self, value):
534 """Add the size of v4 halfopen table memory.
536 :param value: Size of v4 halfopen table memory.
539 path = ['session', 'v4-halfopen-table-memory']
540 self.add_config_item(self._nodeconfig, value, path)
542 def add_session_local_endpoints_table_buckets(self, value):
543 """Add the number of local endpoints table buckets.
545 :param value: Number of local endpoints table buckets.
548 path = ['session', 'local-endpoints-table-buckets']
549 self.add_config_item(self._nodeconfig, value, path)
551 def add_session_local_endpoints_table_memory(self, value):
552 """Add the size of local endpoints table memory.
554 :param value: Size of local endpoints table memory.
557 path = ['session', 'local-endpoints-table-memory']
558 self.add_config_item(self._nodeconfig, value, path)
560 def write_config(self, filename=None):
561 """Generate and write VPP startup configuration to file.
563 Use data from calls to this class to form a startup.conf file and
564 replace /etc/vpp/startup.conf with it on topology node.
566 :param filename: Startup configuration file name.
569 self.dump_config(self._nodeconfig)
572 filename = self._vpp_startup_conf
574 if self._vpp_startup_conf_backup is not None:
575 cmd = ('cp {src} {dest}'.format(
576 src=self._vpp_startup_conf, dest=self._vpp_startup_conf_backup))
578 self._node, cmd, sudo=True, message='Copy config file failed!')
580 cmd = ('echo "{config}" | sudo tee {filename}'.format(
581 config=self._vpp_config, filename=filename))
583 self._node, cmd, message='Writing config file failed!')
585 def apply_config(self, filename=None, verify_vpp=True):
586 """Generate and write VPP startup configuration to file and restart VPP.
588 Use data from calls to this class to form a startup.conf file and
589 replace /etc/vpp/startup.conf with it on topology node.
591 :param filename: Startup configuration file name.
592 :param verify_vpp: Verify VPP is running after restart.
594 :type verify_vpp: bool
596 self.write_config(filename=filename)
598 VPPUtil.restart_vpp_service(self._node)
600 VPPUtil.verify_vpp(self._node)
602 def restore_config(self):
603 """Restore VPP startup.conf from backup."""
604 cmd = ('cp {src} {dest}'.format(
605 src=self._vpp_startup_conf_backup, dest=self._vpp_startup_conf))
607 self._node, cmd, sudo=True, message='Copy config file failed!')