blob: 281c406a027dc094dfa03ab17df7cbba7f2a1a63 [file] [log] [blame]
Michael Walshde791732016-09-06 14:25:24 -05001#!/usr/bin/env python
2
Michael Walsh7423c012016-10-04 10:27:21 -05003r"""
4This module provides many valuable functions such as my_parm_file.
5"""
Michael Walshde791732016-09-06 14:25:24 -05006
7# sys and os are needed to get the program dir path and program name.
8import sys
Michael Walsheaa16852017-09-19 16:30:43 -05009import errno
Michael Walshde791732016-09-06 14:25:24 -050010import os
11import ConfigParser
12import StringIO
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060013import re
Michael Walsh1c85bab2017-05-04 14:29:24 -050014import socket
Michael Walshde791732016-09-06 14:25:24 -050015
Michael Walsh7423c012016-10-04 10:27:21 -050016import gen_print as gp
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060017import gen_cmd as gc
18
Michael Walsh93a09f22017-11-13 15:34:46 -060019robot_env = gp.robot_env
20if robot_env:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060021 from robot.libraries.BuiltIn import BuiltIn
Michael Walsh7423c012016-10-04 10:27:21 -050022
Michael Walshde791732016-09-06 14:25:24 -050023
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060024def add_trailing_slash(dir_path):
Michael Walsh7db77942017-01-10 11:37:06 -060025 r"""
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060026 Add a trailing slash to the directory path if it doesn't already have one
27 and return it.
Michael Walsh7db77942017-01-10 11:37:06 -060028
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060029 Description of arguments:
30 dir_path A directory path.
Michael Walsh7db77942017-01-10 11:37:06 -060031 """
32
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060033 return os.path.normpath(dir_path) + os.path.sep
34
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060035
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060036def which(file_path):
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060037 r"""
38 Find the full path of an executable file and return it.
39
40 The PATH environment variable dictates the results of this function.
41
42 Description of arguments:
43 file_path The relative file path (e.g. "my_file" or
44 "lib/my_file").
45 """
46
47 shell_rc, out_buf = gc.cmd_fnc_u("which " + file_path, quiet=1,
48 print_output=0, show_err=0)
49 if shell_rc != 0:
50 error_message = "Failed to find complete path for file \"" +\
51 file_path + "\".\n"
52 error_message += gp.sprint_var(shell_rc, 1)
53 error_message += out_buf
54 if robot_env:
55 BuiltIn().fail(gp.sprint_error(error_message))
56 else:
57 gp.print_error_report(error_message)
58 return False
59
60 file_path = out_buf.rstrip("\n")
61
62 return file_path
63
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060064
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060065def dft(value, default):
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060066 r"""
67 Return default if value is None. Otherwise, return value.
68
69 This is really just shorthand as shown below.
70
71 dft(value, default)
72
73 vs
74
75 default if value is None else value
76
77 Description of arguments:
78 value The value to be returned.
79 default The default value to return if value is
80 None.
81 """
82
83 return default if value is None else value
84
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060085
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060086def get_mod_global(var_name,
87 default=None,
88 mod_name="__main__"):
Michael Walsh0f2ea5f2017-02-20 15:55:00 -060089 r"""
90 Get module global variable value and return it.
91
92 If we are running in a robot environment, the behavior will default to
93 calling get_variable_value.
94
95 Description of arguments:
96 var_name The name of the variable whose value is
97 sought.
98 default The value to return if the global does not
99 exist.
100 mod_name The name of the module containing the
101 global variable.
102 """
103
104 if robot_env:
105 return BuiltIn().get_variable_value("${" + var_name + "}", default)
106
107 try:
108 module = sys.modules[mod_name]
109 except KeyError:
110 gp.print_error_report("Programmer error - The mod_name passed to" +
111 " this function is invalid:\n" +
112 gp.sprint_var(mod_name))
113 raise ValueError('Programmer error.')
114
115 if default is None:
116 return getattr(module, var_name)
117 else:
118 return getattr(module, var_name, default)
119
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600120
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600121def global_default(var_value,
122 default=0):
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600123 r"""
124 If var_value is not None, return it. Otherwise, return the global
125 variable of the same name, if it exists. If not, return default.
126
127 This is meant for use by functions needing help assigning dynamic default
128 values to their parms. Example:
129
130 def func1(parm1=None):
131
132 parm1 = global_default(parm1, 0)
133
134 Description of arguments:
135 var_value The value being evaluated.
136 default The value to be returned if var_value is
137 None AND the global variable of the same
138 name does not exist.
139 """
140
141 var_name = gp.get_arg_name(0, 1, stack_frame_ix=2)
142
143 return dft(var_value, get_mod_global(var_name, 0))
Michael Walsh7db77942017-01-10 11:37:06 -0600144
Michael Walsh7db77942017-01-10 11:37:06 -0600145
Michael Walsh7db77942017-01-10 11:37:06 -0600146def set_mod_global(var_value,
147 mod_name="__main__",
148 var_name=None):
Michael Walsh7db77942017-01-10 11:37:06 -0600149 r"""
150 Set a global variable for a given module.
151
152 Description of arguments:
153 var_value The value to set in the variable.
154 mod_name The name of the module whose variable is
155 to be set.
156 var_name The name of the variable to set. This
157 defaults to the name of the variable used
158 for var_value when calling this function.
159 """
160
161 try:
162 module = sys.modules[mod_name]
163 except KeyError:
164 gp.print_error_report("Programmer error - The mod_name passed to" +
165 " this function is invalid:\n" +
166 gp.sprint_var(mod_name))
167 raise ValueError('Programmer error.')
168
169 if var_name is None:
170 var_name = gp.get_arg_name(None, 1, 2)
171
172 setattr(module, var_name, var_value)
173
Michael Walsh7db77942017-01-10 11:37:06 -0600174
Michael Walshde791732016-09-06 14:25:24 -0500175def my_parm_file(prop_file_path):
Michael Walshde791732016-09-06 14:25:24 -0500176 r"""
177 Read a properties file, put the keys/values into a dictionary and return
178 the dictionary.
179
180 The properties file must have the following format:
181 var_name<= or :>var_value
182 Comment lines (those beginning with a "#") and blank lines are allowed and
183 will be ignored. Leading and trailing single or double quotes will be
184 stripped from the value. E.g.
185 var1="This one"
186 Quotes are stripped so the resulting value for var1 is:
187 This one
188
189 Description of arguments:
Michael Walsh7423c012016-10-04 10:27:21 -0500190 prop_file_path The caller should pass the path to the
191 properties file.
Michael Walshde791732016-09-06 14:25:24 -0500192 """
193
194 # ConfigParser expects at least one section header in the file (or you
195 # get ConfigParser.MissingSectionHeaderError). Properties files don't
196 # need those so I'll write a dummy section header.
197
198 string_file = StringIO.StringIO()
199 # Write the dummy section header to the string file.
200 string_file.write('[dummysection]\n')
201 # Write the entire contents of the properties file to the string file.
202 string_file.write(open(prop_file_path).read())
203 # Rewind the string file.
204 string_file.seek(0, os.SEEK_SET)
205
206 # Create the ConfigParser object.
207 config_parser = ConfigParser.ConfigParser()
208 # Make the property names case-sensitive.
209 config_parser.optionxform = str
210 # Read the properties from the string file.
211 config_parser.readfp(string_file)
212 # Return the properties as a dictionary.
213 return dict(config_parser.items('dummysection'))
214
Michael Walsh7423c012016-10-04 10:27:21 -0500215
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600216def file_to_list(file_path,
217 newlines=0,
218 comments=1,
219 trim=0):
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600220 r"""
221 Return the contents of a file as a list. Each element of the resulting
222 list is one line from the file.
223
224 Description of arguments:
225 file_path The path to the file (relative or
226 absolute).
227 newlines Include newlines from the file in the
228 results.
229 comments Include comment lines and blank lines in
230 the results. Comment lines are any that
231 begin with 0 or more spaces followed by
232 the pound sign ("#").
233 trim Trim white space from the beginning and
234 end of each line.
235 """
236
237 lines = []
238 file = open(file_path)
239 for line in file:
240 if not comments:
241 if re.match(r"[ ]*#|^$", line):
242 continue
243 if not newlines:
244 line = line.rstrip("\n")
245 if trim:
246 line = line.strip()
247 lines.append(line)
248
249 return lines
250
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600251
Michael Walsh7423c012016-10-04 10:27:21 -0500252def return_path_list():
Michael Walsh7423c012016-10-04 10:27:21 -0500253 r"""
254 This function will split the PATH environment variable into a PATH_LIST
255 and return it. Each element in the list will be normalized and have a
256 trailing slash added.
257 """
258
259 PATH_LIST = os.environ['PATH'].split(":")
260 PATH_LIST = [os.path.normpath(path) + os.sep for path in PATH_LIST]
261
262 return PATH_LIST
263
Michael Walsh7db77942017-01-10 11:37:06 -0600264
Michael Walsh9fac55c2017-09-29 16:53:56 -0500265def escape_bash_quotes(buffer):
Michael Walsh9fac55c2017-09-29 16:53:56 -0500266 r"""
267 Escape quotes in string and return it.
268
269 The escape style implemented will be for use on the bash command line.
270
271 Example:
272 That's all.
273
274 Result:
275 That'\''s all.
276
277 The result may then be single quoted on a bash command. Example:
278
279 echo 'That'\''s all.'
280
281 Description of argument(s):
282 buffer The string whose quotes are to be escaped.
283 """
284
285 return re.sub("\'", "\'\\\'\'", buffer)
286
287
Michael Walsh7db77942017-01-10 11:37:06 -0600288def quote_bash_parm(parm):
Michael Walsh7db77942017-01-10 11:37:06 -0600289 r"""
290 Return the bash command line parm with single quotes if they are needed.
291
292 Description of arguments:
293 parm The string to be quoted.
294 """
295
296 # If any of these characters are found in the parm string, then the
297 # string should be quoted. This list is by no means complete and should
298 # be expanded as needed by the developer of this function.
299 bash_special_chars = set(' $')
300
301 if any((char in bash_special_chars) for char in parm):
302 return "'" + parm + "'"
303
304 return parm
305
Michael Walsh1c85bab2017-05-04 14:29:24 -0500306
Michael Walshf74b3e42018-01-10 11:11:54 -0600307def get_host_name_ip(host,
308 short_name=0):
Michael Walsh1c85bab2017-05-04 14:29:24 -0500309 r"""
310 Get the host name and the IP address for the given host and return them as
311 a tuple.
312
313 Description of argument(s):
Michael Walshd1b6c702017-05-30 17:54:30 -0500314 host The host name or IP address to be obtained.
Michael Walshf74b3e42018-01-10 11:11:54 -0600315 short_name Include the short host name in the
316 returned tuple, i.e. return host, ip and
317 short_host.
Michael Walsh1c85bab2017-05-04 14:29:24 -0500318 """
319
Michael Walshf74b3e42018-01-10 11:11:54 -0600320 host_name = socket.getfqdn(host)
Michael Walshd1b6c702017-05-30 17:54:30 -0500321 try:
322 host_ip = socket.gethostbyname(host)
323 except socket.gaierror as my_gaierror:
324 message = "Unable to obtain the host name for the following host:" +\
325 "\n" + gp.sprint_var(host)
326 gp.print_error_report(message)
327 raise my_gaierror
Michael Walsh1c85bab2017-05-04 14:29:24 -0500328
Michael Walshf74b3e42018-01-10 11:11:54 -0600329 if short_name:
330 host_short_name = host_name.split(".")[0]
331 return host_name, host_ip, host_short_name
332 else:
333 return host_name, host_ip
Michael Walsh1c85bab2017-05-04 14:29:24 -0500334
Michael Walsheaa16852017-09-19 16:30:43 -0500335
336def pid_active(pid):
Michael Walsheaa16852017-09-19 16:30:43 -0500337 r"""
338 Return true if pid represents an active pid and false otherwise.
339
340 Description of argument(s):
341 pid The pid whose status is being sought.
342 """
343
344 try:
345 os.kill(int(pid), 0)
346 except OSError as err:
347 if err.errno == errno.ESRCH:
348 # ESRCH == No such process
349 return False
350 elif err.errno == errno.EPERM:
351 # EPERM clearly means there's a process to deny access to
352 return True
353 else:
354 # According to "man 2 kill" possible error values are
355 # (EINVAL, EPERM, ESRCH)
356 raise
357
358 return True
Michael Walsh112c3592018-06-01 14:15:58 -0500359
360
361def to_signed(number,
362 bit_width=gp.bit_length(long(sys.maxsize)) + 1):
363
364 r"""
365 Convert number to a signed number and return the result.
366
367 Examples:
368
369 With the following code:
370
371 var1 = 0xfffffffffffffff1
372 print_var(var1)
373 print_var(var1, 1)
374 var1 = to_signed(var1)
375 print_var(var1)
376 print_var(var1, 1)
377
378 The following is written to stdout:
379 var1: 18446744073709551601
380 var1: 0x00000000fffffffffffffff1
381 var1: -15
382 var1: 0xfffffffffffffff1
383
384 The same code but with var1 set to 0x000000000000007f produces the
385 following:
386 var1: 127
387 var1: 0x000000000000007f
388 var1: 127
389 var1: 0x000000000000007f
390
391 Description of argument(s):
392 number The number to be converted.
393 bit_width The number of bits that defines a complete
394 hex value. Typically, this would be a
395 multiple of 32.
396 """
397
398 if number < 0:
399 return number
400 neg_bit_mask = 2**(bit_width - 1)
401 if number & neg_bit_mask:
402 return ((2**bit_width) - number) * -1
403 else:
404 return number