Merge "paths: Support binary packages"
[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     }
232
233
234
235     app_version = None
236     app_git_tag = None
237
238     if app_name.lower().startswith('ovs'):
239         app_version = get_bin_version('{} --version'.format(S.getValue('TOOLS')['ovs-vswitchd']),
240                                       app_version_file['ovs'])
241         if 'vswitch_src' in S.getValue('TOOLS'):
242             app_git_tag = get_git_tag(S.getValue('TOOLS')['vswitch_src'])
243     elif app_name.lower() in ['dpdk', 'testpmd']:
244         app_version = get_bin_version('{} -v -h'.format(S.getValue('TOOLS')['testpmd']),
245                                       app_version_file['testpmd'])
246         # we have to consult PATHS settings to be sure, that dpdk/testpmd
247         # were build from the sources
248         if S.getValue('PATHS')[app_name.lower()]['type'] == 'src':
249             app_git_tag = get_git_tag(S.getValue('TOOLS')['dpdk_src'])
250     elif app_name.lower() == 'loopback_testpmd':
251         # testpmd inside the guest is compiled from downloaded sources
252         # stored at TOOS['dpdk_src'] directory
253         tmp_ver = ['', '', '']
254         dpdk_16 = False
255         with open(app_version_file['loopback_testpmd']) as file_:
256             for line in file_:
257                 if not line.strip():
258                     continue
259                 # DPDK version < 16
260                 if line.startswith('#define RTE_VER_MAJOR'):
261                     tmp_ver[0] = line.rstrip('\n').split(' ')[2]
262                 # DPDK version < 16
263                 elif line.startswith('#define RTE_VER_PATCH_LEVEL'):
264                     tmp_ver[2] = line.rstrip('\n').split(' ')[2]
265                 # DPDK version < 16
266                 elif line.startswith('#define RTE_VER_PATCH_RELEASE'):
267                     release = line.rstrip('\n').split(' ')[2]
268                     if not '16' in release:
269                         tmp_ver[2] += line.rstrip('\n').split(' ')[2]
270                 # DPDK all versions
271                 elif line.startswith('#define RTE_VER_MINOR'):
272                     if dpdk_16:
273                         tmp_ver[2] = line.rstrip('\n').split(' ')[2]
274                     else:
275                         tmp_ver[1] = line.rstrip('\n').split(' ')[2]
276                 # DPDK all versions
277                 elif line.startswith('#define RTE_VER_SUFFIX'):
278                     tmp_ver[2] += line.rstrip('\n').split('"')[1]
279                 # DPDK version >= 16
280                 elif line.startswith('#define RTE_VER_YEAR'):
281                     dpdk_16 = True
282                     tmp_ver[0] = line.rstrip('\n').split(' ')[2]
283                 # DPDK version >= 16
284                 elif line.startswith('#define RTE_VER_MONTH'):
285                     tmp_ver[1] = '{:0>2}'.format(line.rstrip('\n').split(' ')[2])
286                 # DPDK version >= 16
287                 elif line.startswith('#define RTE_VER_RELEASE'):
288                     release = line.rstrip('\n').split(' ')[2]
289                     if not '16' in release:
290                         tmp_ver[2] += line.rstrip('\n').split(' ')[2]
291
292         if len(tmp_ver[0]):
293             app_version = '.'.join(tmp_ver)
294         app_git_tag = get_git_tag(S.getValue('TOOLS')['dpdk_src'])
295     elif app_name.lower().startswith('qemu'):
296         app_version = get_bin_version('{} --version'.format(S.getValue('TOOLS')['qemu-system']),
297                                       app_version_file['qemu'])
298         if 'qemu_src' in S.getValue('TOOLS'):
299             app_git_tag = get_git_tag(S.getValue('TOOLS')['qemu_src'])
300     elif app_name.lower() == 'ixnet':
301         app_version = match_line(app_version_file['ixnet'], 'package provide IxTclNetwork')
302         if app_version:
303             app_version = app_version.split(' ')[3]
304     elif app_name.lower() == 'xena':
305         try:
306             app_version = S.getValue('XENA_VERSION')
307         except AttributeError:
308             # setting was not available after execution
309             app_version = 'N/A'
310     elif app_name.lower() == 'dummy':
311         # get git tag of file with Dummy implementation
312         app_git_tag = get_git_tag(os.path.join(S.getValue('ROOT_DIR'), 'tools/pkt_gen/dummy/dummy.py'))
313     elif app_name.lower() == 'vswitchperf':
314         app_git_tag = get_git_tag(S.getValue('ROOT_DIR'))
315     elif app_name.lower() == 'l2fwd':
316         app_version = match_line(app_version_file['loopback_l2fwd'], 'MODULE_VERSION')
317         if app_version:
318             app_version = app_version.split('"')[1]
319         app_git_tag = get_git_tag(app_version_file['loopback_l2fwd'])
320     elif app_name.lower() in ['linux_bridge', 'buildin']:
321         # without login into running VM, it is not possible to check bridge_utils version
322         app_version = 'NA'
323         app_git_tag = 'NA'
324
325     return {'name' : app_name, 'version' : app_version, 'git_tag' : app_git_tag}
326
327 def get_loopback_version(loopback_app_name):
328     """ Get version of given guest loopback application and its git tag
329
330     :returns: dictionary {'name' : app_name, 'version' : app_version, 'git_tag' : app_git_tag) in case that
331         version or git tag are not known or not applicaple, than None is returned for any unknown value
332     """
333     version = get_version("loopback_{}".format(loopback_app_name))
334     version['name'] = loopback_app_name
335     return version