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