| Michael Walsh | de79173 | 2016-09-06 14:25:24 -0500 | [diff] [blame] | 1 | #!/usr/bin/env python | 
|  | 2 |  | 
| Michael Walsh | 7423c01 | 2016-10-04 10:27:21 -0500 | [diff] [blame] | 3 | r""" | 
|  | 4 | This module provides many valuable functions such as my_parm_file. | 
|  | 5 | """ | 
| Michael Walsh | de79173 | 2016-09-06 14:25:24 -0500 | [diff] [blame] | 6 |  | 
|  | 7 | # sys and os are needed to get the program dir path and program name. | 
|  | 8 | import sys | 
| Michael Walsh | eaa1685 | 2017-09-19 16:30:43 -0500 | [diff] [blame] | 9 | import errno | 
| Michael Walsh | de79173 | 2016-09-06 14:25:24 -0500 | [diff] [blame] | 10 | import os | 
|  | 11 | import ConfigParser | 
|  | 12 | import StringIO | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 13 | import re | 
| Michael Walsh | 1c85bab | 2017-05-04 14:29:24 -0500 | [diff] [blame] | 14 | import socket | 
| Michael Walsh | de79173 | 2016-09-06 14:25:24 -0500 | [diff] [blame] | 15 |  | 
| Michael Walsh | 7423c01 | 2016-10-04 10:27:21 -0500 | [diff] [blame] | 16 | import gen_print as gp | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 17 | import gen_cmd as gc | 
|  | 18 |  | 
| Michael Walsh | 93a09f2 | 2017-11-13 15:34:46 -0600 | [diff] [blame] | 19 | robot_env = gp.robot_env | 
|  | 20 | if robot_env: | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 21 | from robot.libraries.BuiltIn import BuiltIn | 
| Michael Walsh | 7423c01 | 2016-10-04 10:27:21 -0500 | [diff] [blame] | 22 |  | 
| Michael Walsh | de79173 | 2016-09-06 14:25:24 -0500 | [diff] [blame] | 23 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 24 | def add_trailing_slash(dir_path): | 
| Michael Walsh | 7db7794 | 2017-01-10 11:37:06 -0600 | [diff] [blame] | 25 |  | 
|  | 26 | r""" | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 27 | Add a trailing slash to the directory path if it doesn't already have one | 
|  | 28 | and return it. | 
| Michael Walsh | 7db7794 | 2017-01-10 11:37:06 -0600 | [diff] [blame] | 29 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 30 | Description of arguments: | 
|  | 31 | dir_path                        A directory path. | 
| Michael Walsh | 7db7794 | 2017-01-10 11:37:06 -0600 | [diff] [blame] | 32 | """ | 
|  | 33 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 34 | return os.path.normpath(dir_path) + os.path.sep | 
|  | 35 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 36 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 37 | def which(file_path): | 
|  | 38 |  | 
|  | 39 | r""" | 
|  | 40 | Find the full path of an executable file and return it. | 
|  | 41 |  | 
|  | 42 | The PATH environment variable dictates the results of this function. | 
|  | 43 |  | 
|  | 44 | Description of arguments: | 
|  | 45 | file_path                       The relative file path (e.g. "my_file" or | 
|  | 46 | "lib/my_file"). | 
|  | 47 | """ | 
|  | 48 |  | 
|  | 49 | shell_rc, out_buf = gc.cmd_fnc_u("which " + file_path, quiet=1, | 
|  | 50 | print_output=0, show_err=0) | 
|  | 51 | if shell_rc != 0: | 
|  | 52 | error_message = "Failed to find complete path for file \"" +\ | 
|  | 53 | file_path + "\".\n" | 
|  | 54 | error_message += gp.sprint_var(shell_rc, 1) | 
|  | 55 | error_message += out_buf | 
|  | 56 | if robot_env: | 
|  | 57 | BuiltIn().fail(gp.sprint_error(error_message)) | 
|  | 58 | else: | 
|  | 59 | gp.print_error_report(error_message) | 
|  | 60 | return False | 
|  | 61 |  | 
|  | 62 | file_path = out_buf.rstrip("\n") | 
|  | 63 |  | 
|  | 64 | return file_path | 
|  | 65 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 66 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 67 | def dft(value, default): | 
|  | 68 |  | 
|  | 69 | r""" | 
|  | 70 | Return default if value is None.  Otherwise, return value. | 
|  | 71 |  | 
|  | 72 | This is really just shorthand as shown below. | 
|  | 73 |  | 
|  | 74 | dft(value, default) | 
|  | 75 |  | 
|  | 76 | vs | 
|  | 77 |  | 
|  | 78 | default if value is None else value | 
|  | 79 |  | 
|  | 80 | Description of arguments: | 
|  | 81 | value                           The value to be returned. | 
|  | 82 | default                         The default value to return if value is | 
|  | 83 | None. | 
|  | 84 | """ | 
|  | 85 |  | 
|  | 86 | return default if value is None else value | 
|  | 87 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 88 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 89 | def get_mod_global(var_name, | 
|  | 90 | default=None, | 
|  | 91 | mod_name="__main__"): | 
|  | 92 |  | 
|  | 93 | r""" | 
|  | 94 | Get module global variable value and return it. | 
|  | 95 |  | 
|  | 96 | If we are running in a robot environment, the behavior will default to | 
|  | 97 | calling get_variable_value. | 
|  | 98 |  | 
|  | 99 | Description of arguments: | 
|  | 100 | var_name                        The name of the variable whose value is | 
|  | 101 | sought. | 
|  | 102 | default                         The value to return if the global does not | 
|  | 103 | exist. | 
|  | 104 | mod_name                        The name of the module containing the | 
|  | 105 | global variable. | 
|  | 106 | """ | 
|  | 107 |  | 
|  | 108 | if robot_env: | 
|  | 109 | return BuiltIn().get_variable_value("${" + var_name + "}", default) | 
|  | 110 |  | 
|  | 111 | try: | 
|  | 112 | module = sys.modules[mod_name] | 
|  | 113 | except KeyError: | 
|  | 114 | gp.print_error_report("Programmer error - The mod_name passed to" + | 
|  | 115 | " this function is invalid:\n" + | 
|  | 116 | gp.sprint_var(mod_name)) | 
|  | 117 | raise ValueError('Programmer error.') | 
|  | 118 |  | 
|  | 119 | if default is None: | 
|  | 120 | return getattr(module, var_name) | 
|  | 121 | else: | 
|  | 122 | return getattr(module, var_name, default) | 
|  | 123 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 124 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 125 | def global_default(var_value, | 
|  | 126 | default=0): | 
|  | 127 |  | 
|  | 128 | r""" | 
|  | 129 | If var_value is not None, return it.  Otherwise, return the global | 
|  | 130 | variable of the same name, if it exists.  If not, return default. | 
|  | 131 |  | 
|  | 132 | This is meant for use by functions needing help assigning dynamic default | 
|  | 133 | values to their parms.  Example: | 
|  | 134 |  | 
|  | 135 | def func1(parm1=None): | 
|  | 136 |  | 
|  | 137 | parm1 = global_default(parm1, 0) | 
|  | 138 |  | 
|  | 139 | Description of arguments: | 
|  | 140 | var_value                       The value being evaluated. | 
|  | 141 | default                         The value to be returned if var_value is | 
|  | 142 | None AND the global variable of the same | 
|  | 143 | name does not exist. | 
|  | 144 | """ | 
|  | 145 |  | 
|  | 146 | var_name = gp.get_arg_name(0, 1, stack_frame_ix=2) | 
|  | 147 |  | 
|  | 148 | return dft(var_value, get_mod_global(var_name, 0)) | 
| Michael Walsh | 7db7794 | 2017-01-10 11:37:06 -0600 | [diff] [blame] | 149 |  | 
| Michael Walsh | 7db7794 | 2017-01-10 11:37:06 -0600 | [diff] [blame] | 150 |  | 
| Michael Walsh | 7db7794 | 2017-01-10 11:37:06 -0600 | [diff] [blame] | 151 | def set_mod_global(var_value, | 
|  | 152 | mod_name="__main__", | 
|  | 153 | var_name=None): | 
|  | 154 |  | 
|  | 155 | r""" | 
|  | 156 | Set a global variable for a given module. | 
|  | 157 |  | 
|  | 158 | Description of arguments: | 
|  | 159 | var_value                       The value to set in the variable. | 
|  | 160 | mod_name                        The name of the module whose variable is | 
|  | 161 | to be set. | 
|  | 162 | var_name                        The name of the variable to set.  This | 
|  | 163 | defaults to the name of the variable used | 
|  | 164 | for var_value when calling this function. | 
|  | 165 | """ | 
|  | 166 |  | 
|  | 167 | try: | 
|  | 168 | module = sys.modules[mod_name] | 
|  | 169 | except KeyError: | 
|  | 170 | gp.print_error_report("Programmer error - The mod_name passed to" + | 
|  | 171 | " this function is invalid:\n" + | 
|  | 172 | gp.sprint_var(mod_name)) | 
|  | 173 | raise ValueError('Programmer error.') | 
|  | 174 |  | 
|  | 175 | if var_name is None: | 
|  | 176 | var_name = gp.get_arg_name(None, 1, 2) | 
|  | 177 |  | 
|  | 178 | setattr(module, var_name, var_value) | 
|  | 179 |  | 
| Michael Walsh | 7db7794 | 2017-01-10 11:37:06 -0600 | [diff] [blame] | 180 |  | 
| Michael Walsh | de79173 | 2016-09-06 14:25:24 -0500 | [diff] [blame] | 181 | def my_parm_file(prop_file_path): | 
|  | 182 |  | 
|  | 183 | r""" | 
|  | 184 | Read a properties file, put the keys/values into a dictionary and return | 
|  | 185 | the dictionary. | 
|  | 186 |  | 
|  | 187 | The properties file must have the following format: | 
|  | 188 | var_name<= or :>var_value | 
|  | 189 | Comment lines (those beginning with a "#") and blank lines are allowed and | 
|  | 190 | will be ignored.  Leading and trailing single or double quotes will be | 
|  | 191 | stripped from the value.  E.g. | 
|  | 192 | var1="This one" | 
|  | 193 | Quotes are stripped so the resulting value for var1 is: | 
|  | 194 | This one | 
|  | 195 |  | 
|  | 196 | Description of arguments: | 
| Michael Walsh | 7423c01 | 2016-10-04 10:27:21 -0500 | [diff] [blame] | 197 | prop_file_path                  The caller should pass the path to the | 
|  | 198 | properties file. | 
| Michael Walsh | de79173 | 2016-09-06 14:25:24 -0500 | [diff] [blame] | 199 | """ | 
|  | 200 |  | 
|  | 201 | # ConfigParser expects at least one section header in the file (or you | 
|  | 202 | # get ConfigParser.MissingSectionHeaderError).  Properties files don't | 
|  | 203 | # need those so I'll write a dummy section header. | 
|  | 204 |  | 
|  | 205 | string_file = StringIO.StringIO() | 
|  | 206 | # Write the dummy section header to the string file. | 
|  | 207 | string_file.write('[dummysection]\n') | 
|  | 208 | # Write the entire contents of the properties file to the string file. | 
|  | 209 | string_file.write(open(prop_file_path).read()) | 
|  | 210 | # Rewind the string file. | 
|  | 211 | string_file.seek(0, os.SEEK_SET) | 
|  | 212 |  | 
|  | 213 | # Create the ConfigParser object. | 
|  | 214 | config_parser = ConfigParser.ConfigParser() | 
|  | 215 | # Make the property names case-sensitive. | 
|  | 216 | config_parser.optionxform = str | 
|  | 217 | # Read the properties from the string file. | 
|  | 218 | config_parser.readfp(string_file) | 
|  | 219 | # Return the properties as a dictionary. | 
|  | 220 | return dict(config_parser.items('dummysection')) | 
|  | 221 |  | 
| Michael Walsh | 7423c01 | 2016-10-04 10:27:21 -0500 | [diff] [blame] | 222 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 223 | def file_to_list(file_path, | 
|  | 224 | newlines=0, | 
|  | 225 | comments=1, | 
|  | 226 | trim=0): | 
|  | 227 |  | 
|  | 228 | r""" | 
|  | 229 | Return the contents of a file as a list.  Each element of the resulting | 
|  | 230 | list is one line from the file. | 
|  | 231 |  | 
|  | 232 | Description of arguments: | 
|  | 233 | file_path                       The path to the file (relative or | 
|  | 234 | absolute). | 
|  | 235 | newlines                        Include newlines from the file in the | 
|  | 236 | results. | 
|  | 237 | comments                        Include comment lines and blank lines in | 
|  | 238 | the results.  Comment lines are any that | 
|  | 239 | begin with 0 or more spaces followed by | 
|  | 240 | the pound sign ("#"). | 
|  | 241 | trim                            Trim white space from the beginning and | 
|  | 242 | end of each line. | 
|  | 243 | """ | 
|  | 244 |  | 
|  | 245 | lines = [] | 
|  | 246 | file = open(file_path) | 
|  | 247 | for line in file: | 
|  | 248 | if not comments: | 
|  | 249 | if re.match(r"[ ]*#|^$", line): | 
|  | 250 | continue | 
|  | 251 | if not newlines: | 
|  | 252 | line = line.rstrip("\n") | 
|  | 253 | if trim: | 
|  | 254 | line = line.strip() | 
|  | 255 | lines.append(line) | 
|  | 256 |  | 
|  | 257 | return lines | 
|  | 258 |  | 
| Michael Walsh | 0f2ea5f | 2017-02-20 15:55:00 -0600 | [diff] [blame] | 259 |  | 
| Michael Walsh | 7423c01 | 2016-10-04 10:27:21 -0500 | [diff] [blame] | 260 | def return_path_list(): | 
|  | 261 |  | 
|  | 262 | r""" | 
|  | 263 | This function will split the PATH environment variable into a PATH_LIST | 
|  | 264 | and return it.  Each element in the list will be normalized and have a | 
|  | 265 | trailing slash added. | 
|  | 266 | """ | 
|  | 267 |  | 
|  | 268 | PATH_LIST = os.environ['PATH'].split(":") | 
|  | 269 | PATH_LIST = [os.path.normpath(path) + os.sep for path in PATH_LIST] | 
|  | 270 |  | 
|  | 271 | return PATH_LIST | 
|  | 272 |  | 
| Michael Walsh | 7db7794 | 2017-01-10 11:37:06 -0600 | [diff] [blame] | 273 |  | 
| Michael Walsh | 9fac55c | 2017-09-29 16:53:56 -0500 | [diff] [blame] | 274 | def escape_bash_quotes(buffer): | 
|  | 275 |  | 
|  | 276 | r""" | 
|  | 277 | Escape quotes in string and return it. | 
|  | 278 |  | 
|  | 279 | The escape style implemented will be for use on the bash command line. | 
|  | 280 |  | 
|  | 281 | Example: | 
|  | 282 | That's all. | 
|  | 283 |  | 
|  | 284 | Result: | 
|  | 285 | That'\''s all. | 
|  | 286 |  | 
|  | 287 | The result may then be single quoted on a bash command.  Example: | 
|  | 288 |  | 
|  | 289 | echo 'That'\''s all.' | 
|  | 290 |  | 
|  | 291 | Description of argument(s): | 
|  | 292 | buffer                          The string whose quotes are to be escaped. | 
|  | 293 | """ | 
|  | 294 |  | 
|  | 295 | return re.sub("\'", "\'\\\'\'", buffer) | 
|  | 296 |  | 
|  | 297 |  | 
| Michael Walsh | 7db7794 | 2017-01-10 11:37:06 -0600 | [diff] [blame] | 298 | def quote_bash_parm(parm): | 
|  | 299 |  | 
|  | 300 | r""" | 
|  | 301 | Return the bash command line parm with single quotes if they are needed. | 
|  | 302 |  | 
|  | 303 | Description of arguments: | 
|  | 304 | parm                            The string to be quoted. | 
|  | 305 | """ | 
|  | 306 |  | 
|  | 307 | # If any of these characters are found in the parm string, then the | 
|  | 308 | # string should be quoted.  This list is by no means complete and should | 
|  | 309 | # be expanded as needed by the developer of this function. | 
|  | 310 | bash_special_chars = set(' $') | 
|  | 311 |  | 
|  | 312 | if any((char in bash_special_chars) for char in parm): | 
|  | 313 | return "'" + parm + "'" | 
|  | 314 |  | 
|  | 315 | return parm | 
|  | 316 |  | 
| Michael Walsh | 1c85bab | 2017-05-04 14:29:24 -0500 | [diff] [blame] | 317 |  | 
| Michael Walsh | 1c85bab | 2017-05-04 14:29:24 -0500 | [diff] [blame] | 318 | def get_host_name_ip(host): | 
|  | 319 |  | 
|  | 320 | r""" | 
|  | 321 | Get the host name and the IP address for the given host and return them as | 
|  | 322 | a tuple. | 
|  | 323 |  | 
|  | 324 | Description of argument(s): | 
| Michael Walsh | d1b6c70 | 2017-05-30 17:54:30 -0500 | [diff] [blame] | 325 | host                            The host name or IP address to be obtained. | 
| Michael Walsh | 1c85bab | 2017-05-04 14:29:24 -0500 | [diff] [blame] | 326 | """ | 
|  | 327 |  | 
|  | 328 | host_host_name = socket.getfqdn(host) | 
| Michael Walsh | d1b6c70 | 2017-05-30 17:54:30 -0500 | [diff] [blame] | 329 | try: | 
|  | 330 | host_ip = socket.gethostbyname(host) | 
|  | 331 | except socket.gaierror as my_gaierror: | 
|  | 332 | message = "Unable to obtain the host name for the following host:" +\ | 
|  | 333 | "\n" + gp.sprint_var(host) | 
|  | 334 | gp.print_error_report(message) | 
|  | 335 | raise my_gaierror | 
| Michael Walsh | 1c85bab | 2017-05-04 14:29:24 -0500 | [diff] [blame] | 336 |  | 
|  | 337 | return host_host_name, host_ip | 
|  | 338 |  | 
| Michael Walsh | eaa1685 | 2017-09-19 16:30:43 -0500 | [diff] [blame] | 339 |  | 
|  | 340 | def pid_active(pid): | 
|  | 341 |  | 
|  | 342 | r""" | 
|  | 343 | Return true if pid represents an active pid and false otherwise. | 
|  | 344 |  | 
|  | 345 | Description of argument(s): | 
|  | 346 | pid                             The pid whose status is being sought. | 
|  | 347 | """ | 
|  | 348 |  | 
|  | 349 | try: | 
|  | 350 | os.kill(int(pid), 0) | 
|  | 351 | except OSError as err: | 
|  | 352 | if err.errno == errno.ESRCH: | 
|  | 353 | # ESRCH == No such process | 
|  | 354 | return False | 
|  | 355 | elif err.errno == errno.EPERM: | 
|  | 356 | # EPERM clearly means there's a process to deny access to | 
|  | 357 | return True | 
|  | 358 | else: | 
|  | 359 | # According to "man 2 kill" possible error values are | 
|  | 360 | # (EINVAL, EPERM, ESRCH) | 
|  | 361 | raise | 
|  | 362 |  | 
|  | 363 | return True |