ixia: Fix IXIA in trafficgen mode
[vswitchperf.git] / tools / systeminfo.py
1 # Copyright 2015-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 """Tools for access to OS details
16 """
17
18 import os
19 import platform
20 import subprocess
21 import locale
22 import re
23 import distro
24
25 from conf import settings as S
26 from tools.version import Version
27
28 def match_line(file_name, pattern):
29     """ loops through given file and returns first line matching given pattern
30
31     :returns: string with the matching line without end of line or None
32     """
33     try:
34         with open(file_name, encoding="latin-1") as file_:
35             for line in file_:
36                 if not line.strip():
37                     continue
38                 if not line.strip().startswith(pattern):
39                     continue
40
41                 return line.strip().rstrip('\n')
42         return None
43     except OSError:
44         return None
45
46 def get_os():
47     """Get distro name.
48
49     :returns: Return distro name as a string
50     """
51     return ' '.join(distro.linux_distribution())
52
53 def get_kernel():
54     """Get kernel version.
55
56     :returns: Return kernel version as a string
57     """
58     return platform.release()
59
60 def get_cpu():
61     """Get CPU information.
62
63     :returns: Return CPU information as a string
64     """
65     cpu = match_line('/proc/cpuinfo', 'model name')
66     return cpu.split(':')[1] if cpu else cpu
67
68 def get_nic():
69     """Get NIC(s) information.
70
71     :returns: Return NIC(s) information as a list
72     """
73     nics = []
74     output = subprocess.check_output('lspci', shell=True)
75     output = output.decode(locale.getdefaultlocale()[1])
76     for line in output.split('\n'):
77         for nic in S.getValue('NICS'):
78             # lspci shows PCI addresses without domain part, i.e. last 7 chars
79             if line.startswith(nic['pci'][-7:]):
80                 nics.append(''.join(line.split(':')[2:]).strip())
81     return nics
82
83 def get_platform():
84     """Get platform information.
85
86     Currently this is the motherboard vendor, name and socket
87     count.
88
89     :returns: Return platform information as a string
90     """
91     output = []
92
93     with open('/sys/class/dmi/id/board_vendor', 'r') as file_:
94         output.append(file_.readline().rstrip())
95
96     with open('/sys/class/dmi/id/board_name', 'r') as file_:
97         output.append(file_.readline().rstrip())
98
99     num_nodes = len([name for name in os.listdir(
100         '/sys/devices/system/node/') if name.startswith('node')])
101     output.append(''.join(['[', str(num_nodes), ' sockets]']))
102
103     return ' '.join(output).strip()
104
105 def get_cpu_cores():
106     """Get number of CPU cores.
107
108     :returns: Return number of CPU cores
109     """
110     cores = 0
111     with open('/proc/cpuinfo') as file_:
112         for line in file_:
113             if line.rstrip('\n').startswith('processor'):
114                 cores += 1
115             continue
116
117     # this code must be executed by at leat one core...
118     if cores < 1:
119         cores = 1
120     return cores
121
122 def get_memory():
123     """Get memory information.
124
125     :returns: amount of system memory as string together with unit
126     """
127     memory = match_line('/proc/meminfo', 'MemTotal')
128     return memory.split(':')[1].strip() if memory else memory
129
130 def get_memory_bytes():
131     """Get memory information in bytes
132
133     :returns: amount of system memory
134     """
135     mem_list = get_memory().split(' ')
136     mem = float(mem_list[0].strip())
137     if mem_list.__len__() > 1:
138         unit = mem_list[1].strip().lower()
139         if unit == 'kb':
140             mem *= 1024
141         elif unit == 'mb':
142             mem *= 1024 ** 2
143         elif unit == 'gb':
144             mem *= 1024 ** 3
145         elif unit == 'tb':
146             mem *= 1024 ** 4
147
148     return int(mem)
149
150 def get_pids(proc_names_list):
151     """ Get pid(s) of process(es) with given name(s)
152
153     :returns: list with pid(s) of given processes or None if processes
154         with given names are not running
155     """
156
157     try:
158         pids = subprocess.check_output(['sudo', 'LC_ALL=' + S.getValue('DEFAULT_CMD_LOCALE'), 'pidof']
159                                        + proc_names_list)
160     except subprocess.CalledProcessError:
161         # such process isn't running
162         return None
163
164     return list(map(str, map(int, pids.split())))
165
166 def get_pid(proc_name_str):
167     """ Get pid(s) of process with given name
168
169     :returns: list with pid(s) of given process or None if process
170         with given name is not running
171     """
172     return get_pids([proc_name_str])
173
174 def pid_isalive(pid):
175     """ Checks if given PID is alive
176
177     :param pid: PID of the process
178     :returns: True if given process is running, False otherwise
179     """
180     return os.path.isdir('/proc/' + str(pid))
181
182 def get_bin_version(binary, regex):
183     """ get version of given binary selected by given regex
184
185     :returns: version string or None
186     """
187     try:
188         output = subprocess.check_output(binary, shell=True).decode().rstrip('\n')
189     except subprocess.CalledProcessError:
190         return None
191
192     versions = re.findall(regex, output)
193     if len(versions):
194         return versions[0]
195     else:
196         return None
197
198 def get_git_tag(path):
199     """ get tag of recent commit from repository located at 'path'
200
201     :returns: git tag in form of string with commit hash or None if there
202         isn't any git repository at given path
203     """
204     try:
205         if os.path.isdir(path):
206             return subprocess.check_output('cd {}; git rev-parse HEAD'.format(path), shell=True,
207                                            stderr=subprocess.DEVNULL).decode().rstrip('\n')
208         elif os.path.isfile(path):
209             return subprocess.check_output('cd $(dirname {}); git log -1 --pretty="%H" {}'.format(path, path),
210                                            shell=True, stderr=subprocess.DEVNULL).decode().rstrip('\n')
211         else:
212             return None
213     except subprocess.CalledProcessError:
214         return None
215
216 # This function uses long switch per purpose, so let us suppress pylint warning too-many-branches
217 # pylint: disable=too-many-branches, too-many-statements
218 def get_version(app_name):
219     """ Get version of given application and its git tag
220
221     :returns: dictionary {'name' : app_name, 'version' : app_version, 'git_tag' : app_git_tag) in case that
222         version or git tag are not known or not applicaple, than None is returned for any unknown value
223
224     """
225     app_version_file = {
226         'ovs' : r'Open vSwitch\) ([0-9.]+)',
227         'testpmd' : r'RTE Version: \'\S+ ([0-9.]+)',
228         'qemu' : r'QEMU emulator version ([0-9.]+)',
229         'loopback_l2fwd' : os.path.join(S.getValue('ROOT_DIR'), 'src/l2fwd/l2fwd.c'),
230         'ixnet' : os.path.join(S.getValue('TRAFFICGEN_IXNET_LIB_PATH'), 'pkgIndex.tcl'),
231         'ixia' : os.path.join(S.getValue('TRAFFICGEN_IXIA_ROOT_DIR'), 'lib/ixTcl1.0/ixTclHal.tcl'),
232     }
233
234
235
236     app_version = None
237     app_git_tag = None
238
239     if app_name.lower().startswith('ovs'):
240         app_version = get_bin_version('{} --version'.format(S.getValue('TOOLS')['ovs-vswitchd']),
241                                       app_version_file['ovs'])
242         if 'vswitch_src' in S.getValue('TOOLS'):
243             app_git_tag = get_git_tag(S.getValue('TOOLS')['vswitch_src'])
244     elif app_name.lower() in ['dpdk', 'testpmd']:
245         app_version = get_bin_version('{} -v -h'.format(S.getValue('TOOLS')['testpmd']),
246                                       app_version_file['testpmd'])
247         # we have to consult PATHS settings to be sure, that dpdk/testpmd
248         # were build from the sources
249         if S.getValue('PATHS')[app_name.lower()]['type'] == 'src':
250             app_git_tag = get_git_tag(S.getValue('TOOLS')['dpdk_src'])
251     elif app_name.lower() == 'loopback_testpmd':
252         # testpmd inside the guest is compiled from downloaded sources
253         # stored at TOOS['dpdk_src'] directory
254         tmp_ver = ['', '', '']
255         dpdk_16 = False
256         # TOOLS dictionary is created during runtime and it is not
257         # available in some vsperf modes (e.g. -m trafficgen), thus
258         # following definition can't be part of app_version_file dict above
259         app_file = os.path.join(S.getValue('TOOLS')['dpdk_src'],
260                                 'lib/librte_eal/common/include/rte_version.h')
261         with open(app_file) as file_:
262             for line in file_:
263                 if not line.strip():
264                     continue
265                 # DPDK version < 16
266                 if line.startswith('#define RTE_VER_MAJOR'):
267                     tmp_ver[0] = line.rstrip('\n').split(' ')[2]
268                 # DPDK version < 16
269                 elif line.startswith('#define RTE_VER_PATCH_LEVEL'):
270                     tmp_ver[2] = line.rstrip('\n').split(' ')[2]
271                 # DPDK version < 16
272                 elif line.startswith('#define RTE_VER_PATCH_RELEASE'):
273                     release = line.rstrip('\n').split(' ')[2]
274                     if not '16' in release:
275                         tmp_ver[2] += line.rstrip('\n').split(' ')[2]
276                 # DPDK all versions
277                 elif line.startswith('#define RTE_VER_MINOR'):
278                     if dpdk_16:
279                         tmp_ver[2] = line.rstrip('\n').split(' ')[2]
280                     else:
281                         tmp_ver[1] = line.rstrip('\n').split(' ')[2]
282                 # DPDK all versions
283                 elif line.startswith('#define RTE_VER_SUFFIX'):
284                     tmp_ver[2] += line.rstrip('\n').split('"')[1]
285                 # DPDK version >= 16
286                 elif line.startswith('#define RTE_VER_YEAR'):
287                     dpdk_16 = True
288                     tmp_ver[0] = line.rstrip('\n').split(' ')[2]
289                 # DPDK version >= 16
290                 elif line.startswith('#define RTE_VER_MONTH'):
291                     tmp_ver[1] = '{:0>2}'.format(line.rstrip('\n').split(' ')[2])
292                 # DPDK version >= 16
293                 elif line.startswith('#define RTE_VER_RELEASE'):
294                     release = line.rstrip('\n').split(' ')[2]
295                     if not '16' in release:
296                         tmp_ver[2] += line.rstrip('\n').split(' ')[2]
297
298         if len(tmp_ver[0]):
299             app_version = '.'.join(tmp_ver)
300         app_git_tag = get_git_tag(S.getValue('TOOLS')['dpdk_src'])
301     elif app_name.lower().startswith('qemu'):
302         app_version = get_bin_version('{} --version'.format(S.getValue('TOOLS')['qemu-system']),
303                                       app_version_file['qemu'])
304         if 'qemu_src' in S.getValue('TOOLS'):
305             app_git_tag = get_git_tag(S.getValue('TOOLS')['qemu_src'])
306     elif app_name.lower() == 'ixnet':
307         app_version = match_line(app_version_file['ixnet'], 'package provide IxTclNetwork')
308         if app_version:
309             app_version = app_version.split(' ')[3]
310     elif app_name.lower() == 'ixia':
311         app_version = match_line(app_version_file['ixia'], 'package provide IxTclHal')
312         if app_version:
313             app_version = app_version.split(' ')[3]
314     elif app_name.lower() == 'xena':
315         try:
316             app_version = S.getValue('XENA_VERSION')
317         except AttributeError:
318             # setting was not available after execution
319             app_version = 'N/A'
320     elif app_name.lower() == 'dummy':
321         # get git tag of file with Dummy implementation
322         app_git_tag = get_git_tag(os.path.join(S.getValue('ROOT_DIR'), 'tools/pkt_gen/dummy/dummy.py'))
323     elif app_name.lower() == 'vswitchperf':
324         app_git_tag = get_git_tag(S.getValue('ROOT_DIR'))
325     elif app_name.lower() == 'l2fwd':
326         app_version = match_line(app_version_file['loopback_l2fwd'], 'MODULE_VERSION')
327         if app_version:
328             app_version = app_version.split('"')[1]
329         app_git_tag = get_git_tag(app_version_file['loopback_l2fwd'])
330     elif app_name.lower() in ['linux_bridge', 'buildin']:
331         # without login into running VM, it is not possible to check bridge_utils version
332         app_version = 'NA'
333         app_git_tag = 'NA'
334
335     return Version(app_name, app_version, app_git_tag)
336
337 def get_loopback_version(loopback_app_name):
338     """ Get version of given guest loopback application and its git tag
339
340     :returns: dictionary {'name' : app_name, 'version' : app_version, 'git_tag' : app_git_tag) in case that
341         version or git tag are not known or not applicaple, than None is returned for any unknown value
342     """
343     version = get_version("loopback_{}".format(loopback_app_name))
344     version.set_value('name', loopback_app_name)
345     return version