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