paths: Modify algorithm for PATHS verification
[vswitchperf.git] / tools / functions.py
1 # Copyright 2016-2017 Intel Corporation.
2 #
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
6 #
7 #   http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 """Various helper functions
16 """
17
18 import os
19 import logging
20 import glob
21 import shutil
22 from conf import settings as S
23
24 MAX_L4_FLOWS = 65536
25
26 #
27 # Support functions
28 #
29 # pylint: disable=too-many-branches
30 def settings_update_paths():
31     """ Configure paths to OVS, DPDK and QEMU sources and binaries based on
32         selected vswitch type and src/binary switch. Data are taken from
33         PATHS dictionary and after their processing they are stored inside TOOLS.
34         PATHS dictionary has specific section for 'vswitch', 'qemu' and 'dpdk'
35         Following processing is done for every item:
36             item 'type' - string, which defines the type of paths ('src' or 'bin') to be selected
37                   for a given section:
38                       'src' means, that VSPERF will use OVS, DPDK or QEMU built from sources
39                           e.g. by execution of systems/build_base_machine.sh script during VSPERF
40                           installation
41                       'bin' means, that VSPERF will use OVS, DPDK or QEMU binaries installed
42                           in the OS, e.g. via OS specific packaging system
43             item 'path' - string with valid path; Its content is checked for existence, prefixed
44                   with section name and stored into TOOLS for later use
45                   e.g. TOOLS['dpdk_src'] or TOOLS['vswitch_src']
46             item 'modules' - list of strings; Every value from given list is checked for '.ko'
47                   suffix. In case it matches and it is not an absolute path to the module, then
48                   module name is prefixed with 'path' defined for the same section
49                   e.g. TOOLS['vswitch_modules'] = [
50                       '/tmp/vsperf/src_vanilla/ovs/ovs/datapath/linux/openvswitch.ko']
51             all other items - string - if given string is a relative path and item 'path'
52                   is defined for a given section, then item content will be prefixed with
53                   content of the 'path'. Otherwise tool name will be searched within
54                   standard system directories. Also any OS filename wildcards will be
55                   expanded to the real path. At the end of processing, every absolute
56                   path is checked for its existence. In case that temporary path (i.e. path
57                   with '_tmp' suffix) doesn't exist, then log will be written and vsperf will
58                   continue. If any other path will not exist, then vsperf execution will
59                   be terminated with runtime error.
60
61         Note: In case that 'bin' type is set for DPDK, then TOOLS['dpdk_src'] will be set to
62         the value of PATHS['dpdk']['src']['path']. The reason is, that VSPERF uses downloaded
63         DPDK sources to copy DPDK and testpmd into the GUEST, where testpmd is built. In case,
64         that DPDK sources are not available, then vsperf will continue with test execution,
65         but testpmd can't be used as a guest loopback. This is useful in case, that other guest
66         loopback applications (e.g. buildin) are used by CI jobs, etc.
67     """
68     # set dpdk and ovs paths according to VNF, VSWITCH and TRAFFICGEN selection
69     paths = {}
70     if S.getValue("mode") != 'trafficgen':
71         # VSWITCH & (probably) VNF are needed
72         vswitch_type = S.getValue('PATHS')['vswitch'][S.getValue('VSWITCH')]['type']
73         paths['vswitch'] = S.getValue('PATHS')['vswitch'][S.getValue('VSWITCH')][vswitch_type]
74         paths['dpdk'] = S.getValue('PATHS')['dpdk'][S.getValue('PATHS')['dpdk']['type']]
75         paths['qemu'] = S.getValue('PATHS')['qemu'][S.getValue('PATHS')['qemu']['type']]
76         paths['paths'] = {}
77         paths['paths']['ovs_var_tmp'] = S.getValue('PATHS')['vswitch']['ovs_var_tmp']
78         paths['paths']['ovs_etc_tmp'] = S.getValue('PATHS')['vswitch']['ovs_etc_tmp']
79
80     if S.getValue("mode") != 'trafficgen-off':
81         # TRAFFCIGEN is required
82         if S.getValue('TRAFFICGEN') in S.getValue('PATHS')['trafficgen']:
83             tmp_trafficgen = S.getValue('PATHS')['trafficgen'][S.getValue('TRAFFICGEN')]
84             paths['trafficgen'] = tmp_trafficgen[tmp_trafficgen['type']]
85
86     tools = {}
87     # pylint: disable=too-many-nested-blocks
88     for path_class in paths:
89         for tool in paths[path_class]:
90             tmp_tool = paths[path_class][tool]
91
92             # store valid path of given class into tools dict
93             if tool == 'path':
94                 if os.path.isdir(tmp_tool):
95                     tools['{}_src'.format(path_class)] = tmp_tool
96                     continue
97                 else:
98                     raise RuntimeError('Path {} does not exist.'.format(tmp_tool))
99
100             # store list of modules of given class into tools dict
101             if tool == 'modules':
102                 tmp_modules = []
103                 for module in tmp_tool:
104                     # add path to the .ko modules and check it for existence
105                     if module.endswith('.ko') and not os.path.isabs(module):
106                         module = os.path.join(paths[path_class]['path'], module)
107                         if not os.path.exists(module):
108                             raise RuntimeError('Cannot locate modlue {}'.format(module))
109
110                     tmp_modules.append(module)
111
112                 tools['{}_modules'.format(path_class)] = tmp_modules
113                 continue
114
115             # if path to the tool is relative, then 'path' will be prefixed
116             # in case that 'path' is not defined, then tool will be searched
117             # within standard system paths
118             if not os.path.isabs(tmp_tool):
119                 if 'path' in paths[path_class]:
120                     tmp_tool = os.path.join(paths[path_class]['path'], tmp_tool)
121                 elif shutil.which(tmp_tool):
122                     tmp_tool = shutil.which(tmp_tool)
123                 else:
124                     raise RuntimeError('Cannot locate tool {}'.format(tmp_tool))
125
126             # expand OS wildcards in paths if needed
127             if glob.has_magic(tmp_tool):
128                 tmp_glob = glob.glob(tmp_tool)
129                 if len(tmp_glob) == 0:
130                     raise RuntimeError('Path to the {} is not valid: {}.'.format(tool, tmp_tool))
131                 elif len(tmp_glob) > 1:
132                     raise RuntimeError('Path to the {} is ambiguous {}'.format(tool, tmp_glob))
133                 elif len(tmp_glob) == 1:
134                     tmp_tool = tmp_glob[0]
135             elif not os.path.exists(tmp_tool):
136                 if tool.endswith('_tmp'):
137                     logging.getLogger().debug('Temporary path to the %s does not '
138                                               'exist: %s', tool, tmp_tool)
139                 else:
140                     raise RuntimeError('Path to the {} is not valid: {}'.format(tool, tmp_tool))
141
142             tools[tool] = tmp_tool
143
144     # ensure, that dpkg_src for bin will be set to downloaded DPDK sources, so it can
145     # be copied to the guest share dir and used by GUEST to build and run testpmd
146     # Validity of the path is not checked by purpose, so user can use VSPERF without
147     # downloading DPDK sources. In that case guest loopback can't be set to 'testpmd'
148     if S.getValue('PATHS')['dpdk']['type'] == 'bin':
149         tools['dpdk_src'] = S.getValue('PATHS')['dpdk']['src']['path']
150
151     S.setValue('TOOLS', tools)
152
153 def check_traffic(traffic):
154     """Check traffic definition and correct it if possible.
155     """
156     # check if requested networking layers make sense
157     if traffic['l4']['enabled']:
158         if not traffic['l3']['enabled']:
159             raise RuntimeError('TRAFFIC misconfiguration: l3 must be enabled '
160                                'if l4 is enabled.')
161
162     # check if multistream configuration makes sense
163     if traffic['multistream']:
164         if traffic['stream_type'] == 'L3':
165             if not traffic['l3']['enabled']:
166                 raise RuntimeError('TRAFFIC misconfiguration: l3 must be '
167                                    'enabled if l3 streams are requested.')
168         if traffic['stream_type'] == 'L4':
169             if not traffic['l4']['enabled']:
170                 raise RuntimeError('TRAFFIC misconfiguration: l4 must be '
171                                    'enabled if l4 streams are requested.')
172
173             # in case of UDP ports we have only 65536 (0-65535) unique options
174             if traffic['multistream'] > MAX_L4_FLOWS:
175                 logging.getLogger().warning(
176                     'Requested amount of L4 flows %s is bigger than number of '
177                     'transport protocol ports. It was set to %s.',
178                     traffic['multistream'], MAX_L4_FLOWS)
179                 traffic['multistream'] = MAX_L4_FLOWS
180
181     return traffic