179e4a02c888a54d14fe87729e413754bb5c0e1e
[csit.git] / resources / libraries / python / VppConfigGenerator.py
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:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
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.
13
14 """VPP Configuration File Generator library."""
15
16 import re
17
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
22
23 __all__ = ['VppConfigGenerator']
24
25
26 def pci_dev_check(pci_dev):
27     """Check if provided PCI address is in correct format.
28
29     :param pci_dev: PCI address (expected format: xxxx:xx:xx.x).
30     :type pci_dev: str
31     :returns: True if PCI address is in correct format.
32     :rtype: bool
33     :raises ValueError: If PCI address is in incorrect format.
34     """
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))
40     return True
41
42
43 class VppConfigGenerator(object):
44     """VPP Configuration File Generator."""
45
46     def __init__(self):
47         """Initialize library."""
48         # VPP Node to apply configuration on
49         self._node = ''
50         # VPP Hostname
51         self._hostname = ''
52         # VPP Configuration
53         self._nodeconfig = {}
54         # Serialized VPP Configuration
55         self._vpp_config = ''
56         # VPP Service name
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
64
65     def set_node(self, node):
66         """Set DUT node.
67
68         :param node: Node to store configuration on.
69         :type node: dict
70         :raises RuntimeError: If Node type is not DUT.
71         """
72         if node['type'] != NodeType.DUT:
73             raise RuntimeError('Startup config can only be applied to DUT'
74                                'node.')
75         self._node = node
76         self._hostname = Topology.get_node_hostname(node)
77
78     def set_vpp_logfile(self, logfile):
79         """Set VPP logfile location.
80
81         :param logfile: VPP logfile location.
82         :type logfile: str
83         """
84         self._vpp_logfile = logfile
85
86     def set_vpp_startup_conf_backup(self, backup='/etc/vpp/startup.backup'):
87         """Set VPP startup configuration backup.
88
89         :param backup: VPP logfile location.
90         :type backup: str
91         """
92         self._vpp_startup_conf_backup = backup
93
94     def get_config_str(self):
95         """Get dumped startup configuration in VPP config format.
96
97         :returns: Startup configuration in VPP config format.
98         :rtype: str
99         """
100         self.dump_config(self._nodeconfig)
101         return self._vpp_config
102
103     def add_config_item(self, config, value, path):
104         """Add startup configuration item.
105
106         :param config: Startup configuration of node.
107         :param value: Value to insert.
108         :param path: Path where to insert item.
109         :type config: dict
110         :type value: str
111         :type path: list
112         """
113         if len(path) == 1:
114             config[path[0]] = value
115             return
116         if path[0] not in config:
117             config[path[0]] = {}
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:])
122
123     def dump_config(self, obj, level=-1):
124         """Dump the startup configuration in VPP config format.
125
126         :param obj: Python Object to print.
127         :param level: Nested level for indentation.
128         :type obj: Obj
129         :type level: int
130         :returns: nothing
131         """
132         indent = '  '
133         if level >= 0:
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,
139                                                         key)
140                     self.dump_config(val, level + 1)
141                 else:
142                     self._vpp_config += '{}{} {}\n'.format((level + 1) * indent,
143                                                            key, val)
144         else:
145             for val in obj:
146                 self._vpp_config += '{}{}\n'.format((level + 1) * indent, val)
147         if level >= 0:
148             self._vpp_config += '{}}}\n'.format(level * indent)
149
150     def add_unix_log(self, value=None):
151         """Add UNIX log configuration.
152
153         :param value: Log file.
154         :type value: str
155         """
156         path = ['unix', 'log']
157         if value is None:
158             value = self._vpp_logfile
159         self.add_config_item(self._nodeconfig, value, path)
160
161     def add_unix_cli_listen(self, value='/run/vpp/cli.sock'):
162         """Add UNIX cli-listen configuration.
163
164         :param value: CLI listen address and port or path to CLI socket.
165         :type value: str
166         """
167         path = ['unix', 'cli-listen']
168         self.add_config_item(self._nodeconfig, value, path)
169
170     def add_unix_gid(self, value='vpp'):
171         """Add UNIX gid configuration.
172
173         :param value: Gid.
174         :type value: str
175         """
176         path = ['unix', 'gid']
177         self.add_config_item(self._nodeconfig, value, path)
178
179     def add_unix_nodaemon(self):
180         """Add UNIX nodaemon configuration."""
181         path = ['unix', 'nodaemon']
182         self.add_config_item(self._nodeconfig, '', path)
183
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)
188
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)
193
194     def add_api_segment_gid(self, value='vpp'):
195         """Add API-SEGMENT gid configuration.
196
197         :param value: Gid.
198         :type value: str
199         """
200         path = ['api-segment', 'gid']
201         self.add_config_item(self._nodeconfig, value, path)
202
203     def add_api_segment_global_size(self, value):
204         """Add API-SEGMENT global-size configuration.
205
206         :param value: Global size.
207         :type value: str
208         """
209         path = ['api-segment', 'global-size']
210         self.add_config_item(self._nodeconfig, value, path)
211
212     def add_api_segment_api_size(self, value):
213         """Add API-SEGMENT api-size configuration.
214
215         :param value: API size.
216         :type value: str
217         """
218         path = ['api-segment', 'api-size']
219         self.add_config_item(self._nodeconfig, value, path)
220
221     def add_buffers_per_numa(self, value):
222         """Increase number of buffers allocated.
223
224         :param value: Number of buffers allocated.
225         :type value: int
226         """
227         path = ['buffers', 'buffers-per-numa']
228         self.add_config_item(self._nodeconfig, value, path)
229
230     def add_dpdk_dev(self, *devices):
231         """Add DPDK PCI device configuration.
232
233         :param devices: PCI device(s) (format xxxx:xx:xx.x)
234         :type devices: tuple
235         """
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)
240
241     def add_dpdk_dev_parameter(self, device, parameter, value):
242         """Add parameter for DPDK device.
243
244         :param device: PCI device (format xxxx:xx:xx.x).
245         :param parameter: Parameter name.
246         :param value: Parameter value.
247         :type device: str
248         :type parameter: str
249         :type value: str
250         """
251         if pci_dev_check(device):
252             path = ['dpdk', 'dev {0}'.format(device), parameter]
253             self.add_config_item(self._nodeconfig, value, path)
254
255     def add_dpdk_cryptodev(self, count):
256         """Add DPDK Crypto PCI device configuration.
257
258         :param count: Number of HW crypto devices to add.
259         :type count: int
260         """
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')
268
269     def add_dpdk_sw_cryptodev(self, sw_pmd_type, socket_id, count):
270         """Add DPDK SW Crypto device configuration.
271
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
276         :type socket_id: int
277         :type count: int
278         """
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)
284
285     def add_dpdk_eth_bond_dev(self, ethbond_id, mode, xmit_policy, *slaves):
286         """Add DPDK Eth_bond device configuration.
287
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
295         :type slaves: list
296         """
297         slaves_config = ',slave=' + \
298                         ',slave='.join(slave if pci_dev_check(slave) else ''
299                                        for slave in slaves)
300         ethbond_config = 'vdev eth_bond{id},mode={mode}{slaves},' \
301                          'xmit_policy={xmit_pol}'.format(id=ethbond_id,
302                                                          mode=mode,
303                                                          slaves=slaves_config,
304                                                          xmit_pol=xmit_policy)
305         path = ['dpdk', ethbond_config]
306         self.add_config_item(self._nodeconfig, '', path)
307
308     def add_dpdk_dev_default_rxq(self, value):
309         """Add DPDK dev default rxq configuration.
310
311         :param value: Default number of rxqs.
312         :type value: str
313         """
314         path = ['dpdk', 'dev default', 'num-rx-queues']
315         self.add_config_item(self._nodeconfig, value, path)
316
317     def add_dpdk_dev_default_txq(self, value):
318         """Add DPDK dev default txq configuration.
319
320         :param value: Default number of txqs.
321         :type value: str
322         """
323         path = ['dpdk', 'dev default', 'num-tx-queues']
324         self.add_config_item(self._nodeconfig, value, path)
325
326     def add_dpdk_dev_default_rxd(self, value):
327         """Add DPDK dev default rxd configuration.
328
329         :param value: Default number of rxds.
330         :type value: str
331         """
332         path = ['dpdk', 'dev default', 'num-rx-desc']
333         self.add_config_item(self._nodeconfig, value, path)
334
335     def add_dpdk_dev_default_txd(self, value):
336         """Add DPDK dev default txd configuration.
337
338         :param value: Default number of txds.
339         :type value: str
340         """
341         path = ['dpdk', 'dev default', 'num-tx-desc']
342         self.add_config_item(self._nodeconfig, value, path)
343
344     def add_dpdk_log_level(self, value):
345         """Add DPDK log-level configuration.
346
347         :param value: Log level.
348         :type value: str
349         """
350         path = ['dpdk', 'log-level']
351         self.add_config_item(self._nodeconfig, value, path)
352
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)
357
358     def add_dpdk_uio_driver(self, value=None):
359         """Add DPDK uio-driver configuration.
360
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.
364         :type value: str
365         """
366         if value is None:
367             value = Topology.get_uio_driver(self._node)
368         path = ['dpdk', 'uio-driver']
369         self.add_config_item(self._nodeconfig, value, path)
370
371     def add_cpu_main_core(self, value):
372         """Add CPU main core configuration.
373
374         :param value: Main core option.
375         :type value: str
376         """
377         path = ['cpu', 'main-core']
378         self.add_config_item(self._nodeconfig, value, path)
379
380     def add_cpu_corelist_workers(self, value):
381         """Add CPU corelist-workers configuration.
382
383         :param value: Corelist-workers option.
384         :type value: str
385         """
386         path = ['cpu', 'corelist-workers']
387         self.add_config_item(self._nodeconfig, value, path)
388
389     def add_heapsize(self, value):
390         """Add Heapsize configuration.
391
392         :param value: Amount of heapsize.
393         :type value: str
394         """
395         path = ['heapsize']
396         self.add_config_item(self._nodeconfig, value, path)
397
398     def add_api_trace(self):
399         """Add API trace configuration."""
400         path = ['api-trace', 'on']
401         self.add_config_item(self._nodeconfig, '', path)
402
403     def add_ip6_hash_buckets(self, value):
404         """Add IP6 hash buckets configuration.
405
406         :param value: Number of IP6 hash buckets.
407         :type value: str
408         """
409         path = ['ip6', 'hash-buckets']
410         self.add_config_item(self._nodeconfig, value, path)
411
412     def add_ip6_heap_size(self, value):
413         """Add IP6 heap-size configuration.
414
415         :param value: IP6 Heapsize amount.
416         :type value: str
417         """
418         path = ['ip6', 'heap-size']
419         self.add_config_item(self._nodeconfig, value, path)
420
421     def add_ip_heap_size(self, value):
422         """Add IP heap-size configuration.
423
424         :param value: IP Heapsize amount.
425         :type value: str
426         """
427         path = ['ip', 'heap-size']
428         self.add_config_item(self._nodeconfig, value, path)
429
430     def add_statseg_size(self, value):
431         """Add stats segment heap size configuration.
432
433         :param value: Stats heapsize amount.
434         :type value: str
435         """
436         path = ['statseg', 'size']
437         self.add_config_item(self._nodeconfig, value, path)
438
439     def add_plugin(self, state, *plugins):
440         """Add plugin section for specific plugin(s).
441
442         :param state: State of plugin [enable|disable].
443         :param plugins: Plugin(s) to disable.
444         :type state: str
445         :type plugins: list
446         """
447         for plugin in plugins:
448             path = ['plugins', 'plugin {0}'.format(plugin), state]
449             self.add_config_item(self._nodeconfig, ' ', path)
450
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)
455
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)
460
461     def add_nat(self, value='deterministic'):
462         """Add NAT configuration.
463
464         :param value: NAT mode.
465         :type value: str
466         """
467         path = ['nat']
468         self.add_config_item(self._nodeconfig, value, path)
469
470     def add_tcp_preallocated_connections(self, value):
471         """Add TCP pre-allocated connections.
472
473         :param value: The number of pre-allocated connections.
474         :type value: int
475         """
476         path = ['tcp', 'preallocated-connections']
477         self.add_config_item(self._nodeconfig, value, path)
478
479     def add_tcp_preallocated_half_open_connections(self, value):
480         """Add TCP pre-allocated half open connections.
481
482         :param value: The number of pre-allocated half open connections.
483         :type value: int
484         """
485         path = ['tcp', 'preallocated-half-open-connections']
486         self.add_config_item(self._nodeconfig, value, path)
487
488     def add_session_event_queue_length(self, value):
489         """Add session event queue length.
490
491         :param value: Session event queue length.
492         :type value: int
493         """
494         path = ['session', 'event-queue-length']
495         self.add_config_item(self._nodeconfig, value, path)
496
497     def add_session_preallocated_sessions(self, value):
498         """Add the number of pre-allocated sessions.
499
500         :param value: Number of pre-allocated sessions.
501         :type value: int
502         """
503         path = ['session', 'preallocated-sessions']
504         self.add_config_item(self._nodeconfig, value, path)
505
506     def add_session_v4_session_table_buckets(self, value):
507         """Add number of v4 session table buckets to the config.
508
509         :param value: Number of v4 session table buckets.
510         :type value: int
511         """
512         path = ['session', 'v4-session-table-buckets']
513         self.add_config_item(self._nodeconfig, value, path)
514
515     def add_session_v4_session_table_memory(self, value):
516         """Add the size of v4 session table memory.
517
518         :param value: Size of v4 session table memory.
519         :type value: str
520         """
521         path = ['session', 'v4-session-table-memory']
522         self.add_config_item(self._nodeconfig, value, path)
523
524     def add_session_v4_halfopen_table_buckets(self, value):
525         """Add the number of v4 halfopen table buckets.
526
527         :param value: Number of v4 halfopen table buckets.
528         :type value: int
529         """
530         path = ['session', 'v4-halfopen-table-buckets']
531         self.add_config_item(self._nodeconfig, value, path)
532
533     def add_session_v4_halfopen_table_memory(self, value):
534         """Add the size of v4 halfopen table memory.
535
536         :param value: Size of v4 halfopen table memory.
537         :type value: str
538         """
539         path = ['session', 'v4-halfopen-table-memory']
540         self.add_config_item(self._nodeconfig, value, path)
541
542     def add_session_local_endpoints_table_buckets(self, value):
543         """Add the number of local endpoints table buckets.
544
545         :param value: Number of local endpoints table buckets.
546         :type value: int
547         """
548         path = ['session', 'local-endpoints-table-buckets']
549         self.add_config_item(self._nodeconfig, value, path)
550
551     def add_session_local_endpoints_table_memory(self, value):
552         """Add the size of local endpoints table memory.
553
554         :param value: Size of local endpoints table memory.
555         :type value: str
556         """
557         path = ['session', 'local-endpoints-table-memory']
558         self.add_config_item(self._nodeconfig, value, path)
559
560     def write_config(self, filename=None):
561         """Generate and write VPP startup configuration to file.
562
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.
565
566         :param filename: Startup configuration file name.
567         :type filename: str
568         """
569         self.dump_config(self._nodeconfig)
570
571         if filename is None:
572             filename = self._vpp_startup_conf
573
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))
577             exec_cmd_no_error(
578                 self._node, cmd, sudo=True, message='Copy config file failed!')
579
580         cmd = ('echo "{config}" | sudo tee {filename}'.format(
581             config=self._vpp_config, filename=filename))
582         exec_cmd_no_error(
583             self._node, cmd, message='Writing config file failed!')
584
585     def apply_config(self, filename=None, verify_vpp=True):
586         """Generate and write VPP startup configuration to file and restart VPP.
587
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.
590
591         :param filename: Startup configuration file name.
592         :param verify_vpp: Verify VPP is running after restart.
593         :type filename: str
594         :type verify_vpp: bool
595         """
596         self.write_config(filename=filename)
597
598         VPPUtil.restart_vpp_service(self._node)
599         if verify_vpp:
600             VPPUtil.verify_vpp(self._node)
601
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))
606         exec_cmd_no_error(
607             self._node, cmd, sudo=True, message='Copy config file failed!')

©2016 FD.io a Linux Foundation Collaborative Project. All Rights Reserved.
Linux Foundation is a registered trademark of The Linux Foundation. Linux is a registered trademark of Linus Torvalds.
Please see our privacy policy and terms of use.