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