1#!/usr/bin/env python 2 3r""" 4Define variable manipulation functions. 5""" 6 7import os 8import re 9 10try: 11 from robot.utils import DotDict 12except ImportError: 13 pass 14 15import collections 16 17import gen_print as gp 18import gen_misc as gm 19 20 21def create_var_dict(*args): 22 23 r""" 24 Create a dictionary whose keys/values are the arg names/arg values passed 25 to it and return it to the caller. 26 27 Note: The resulting dictionary will be ordered. 28 29 Description of argument(s): 30 *args An unlimited number of arguments to be processed. 31 32 Example use: 33 34 first_name = 'Steve' 35 last_name = 'Smith' 36 var_dict = create_var_dict(first_name, last_name) 37 38 gp.print_var(var_dict) 39 40 The print-out of the resulting var dictionary is: 41 var_dict: 42 var_dict[first_name]: Steve 43 var_dict[last_name]: Smith 44 """ 45 46 try: 47 result_dict = collections.OrderedDict() 48 except AttributeError: 49 result_dict = DotDict() 50 51 arg_num = 1 52 for arg in args: 53 arg_name = gp.get_arg_name(None, arg_num, stack_frame_ix=2) 54 result_dict[arg_name] = arg 55 arg_num += 1 56 57 return result_dict 58 59 60default_record_delim = ':' 61default_key_val_delim = '.' 62 63 64def join_dict(dict, 65 record_delim=default_record_delim, 66 key_val_delim=default_key_val_delim): 67 68 r""" 69 Join a dictionary's keys and values into a string and return the string. 70 71 Description of argument(s): 72 dict The dictionary whose keys and values are 73 to be joined. 74 record_delim The delimiter to be used to separate 75 dictionary pairs in the resulting string. 76 key_val_delim The delimiter to be used to separate keys 77 from values in the resulting string. 78 79 Example use: 80 81 gp.print_var(var_dict) 82 str1 = join_dict(var_dict) 83 gp.pvar(str1) 84 85 Program output. 86 var_dict: 87 var_dict[first_name]: Steve 88 var_dict[last_name]: Smith 89 str1: 90 first_name.Steve:last_name.Smith 91 """ 92 93 format_str = '%s' + key_val_delim + '%s' 94 return record_delim.join([format_str % (key, value) for (key, value) in 95 dict.items()]) 96 97 98def split_to_dict(string, 99 record_delim=default_record_delim, 100 key_val_delim=default_key_val_delim): 101 102 r""" 103 Split a string into a dictionary and return it. 104 105 This function is the complement to join_dict. 106 107 Description of argument(s): 108 string The string to be split into a dictionary. 109 The string must have the proper delimiters 110 in it. A string created by join_dict 111 would qualify. 112 record_delim The delimiter to be used to separate 113 dictionary pairs in the input string. 114 key_val_delim The delimiter to be used to separate 115 keys/values in the input string. 116 117 Example use: 118 119 gp.print_var(str1) 120 new_dict = split_to_dict(str1) 121 gp.print_var(new_dict) 122 123 124 Program output. 125 str1: 126 first_name.Steve:last_name.Smith 127 new_dict: 128 new_dict[first_name]: Steve 129 new_dict[last_name]: Smith 130 """ 131 132 try: 133 result_dict = collections.OrderedDict() 134 except AttributeError: 135 result_dict = DotDict() 136 137 raw_keys_values = string.split(record_delim) 138 for key_value in raw_keys_values: 139 key_value_list = key_value.split(key_val_delim) 140 try: 141 result_dict[key_value_list[0]] = key_value_list[1] 142 except IndexError: 143 result_dict[key_value_list[0]] = "" 144 145 return result_dict 146 147 148def create_file_path(file_name_dict, 149 dir_path="/tmp/", 150 file_suffix=""): 151 152 r""" 153 Create a file path using the given parameters and return it. 154 155 Description of argument(s): 156 file_name_dict A dictionary with keys/values which are to 157 appear as part of the file name. 158 dir_path The dir_path that is to appear as part of 159 the file name. 160 file_suffix A suffix to be included as part of the 161 file name. 162 """ 163 164 dir_path = gm.add_trailing_slash(dir_path) 165 return dir_path + join_dict(file_name_dict) + file_suffix 166 167 168def parse_file_path(file_path): 169 170 r""" 171 Parse a file path created by create_file_path and return the result as a 172 dictionary. 173 174 This function is the complement to create_file_path. 175 176 Description of argument(s): 177 file_path The file_path. 178 179 Example use: 180 gp.pvar(boot_results_file_path) 181 file_path_data = parse_file_path(boot_results_file_path) 182 gp.pvar(file_path_data) 183 184 Program output. 185 186 boot_results_file_path: 187 /tmp/pgm_name.obmc_boot_test:openbmc_nickname.beye6:master_pid.2039:boot_re 188 sults 189 file_path_data: 190 file_path_data[dir_path]: /tmp/ 191 file_path_data[pgm_name]: obmc_boot_test 192 file_path_data[openbmc_nickname]: beye6 193 file_path_data[master_pid]: 2039 194 file_path_data[boot_results]: 195 """ 196 197 try: 198 result_dict = collections.OrderedDict() 199 except AttributeError: 200 result_dict = DotDict() 201 202 dir_path = os.path.dirname(file_path) + os.sep 203 file_path = os.path.basename(file_path) 204 205 result_dict['dir_path'] = dir_path 206 207 result_dict.update(split_to_dict(file_path)) 208 209 return result_dict 210 211 212def parse_key_value(string, 213 delim=":", 214 strip=" ", 215 to_lower=1, 216 underscores=1): 217 218 r""" 219 Parse a key/value string and return as a key/value tuple. 220 221 This function is useful for parsing a line of program output or data that 222 is in the following form: 223 <key or variable name><delimiter><value> 224 225 An example of a key/value string would be as follows: 226 227 Current Limit State: No Active Power Limit 228 229 In the example shown, the delimiter is ":". The resulting key would be as 230 follows: 231 Current Limit State 232 233 Note: If one were to take the default values of to_lower=1 and 234 underscores=1, the resulting key would be as follows: 235 current_limit_state 236 237 The to_lower and underscores arguments are provided for those who wish to 238 have their key names have the look and feel of python variable names. 239 240 The resulting value for the example above would be as follows: 241 No Active Power Limit 242 243 Another example: 244 name=Mike 245 246 In this case, the delim would be "=", the key is "name" and the value is 247 "Mike". 248 249 Description of argument(s): 250 string The string to be parsed. 251 delim The delimiter which separates the key from 252 the value. 253 strip The characters (if any) to strip from the 254 beginning and end of both the key and the 255 value. 256 to_lower Change the key name to lower case. 257 underscores Change any blanks found in the key name to 258 underscores. 259 """ 260 261 pair = string.split(delim) 262 263 key = pair[0].strip(strip) 264 if len(pair) == 0: 265 value = "" 266 else: 267 value = "".join(pair[1:]).strip(strip) 268 269 if to_lower: 270 key = key.lower() 271 if underscores: 272 key = re.sub(r" ", "_", key) 273 274 return key, value 275 276 277def key_value_list_to_dict(list, 278 **args): 279 280 r""" 281 Convert a list containing key/value strings to a dictionary and return it. 282 283 See docstring of parse_key_value function for details on key/value strings. 284 285 Example usage: 286 287 For the following value of list: 288 289 list: 290 list[0]: Current Limit State: No Active Power Limit 291 list[1]: Exception actions: Hard Power Off & Log Event to SEL 292 list[2]: Power Limit: 0 Watts 293 list[3]: Correction time: 0 milliseconds 294 list[4]: Sampling period: 0 seconds 295 296 And the following call in python: 297 298 power_limit = key_value_outbuf_to_dict(list) 299 300 The resulting power_limit directory would look like this: 301 302 power_limit: 303 [current_limit_state]: No Active Power Limit 304 [exception_actions]: Hard Power Off & Log Event to SEL 305 [power_limit]: 0 Watts 306 [correction_time]: 0 milliseconds 307 [sampling_period]: 0 seconds 308 309 Description of argument(s): 310 list A list of key/value strings. (See 311 docstring of parse_key_value function for 312 details). 313 **args Arguments to be interpreted by 314 parse_key_value. (See docstring of 315 parse_key_value function for details). 316 """ 317 318 try: 319 result_dict = collections.OrderedDict() 320 except AttributeError: 321 result_dict = DotDict() 322 323 for entry in list: 324 key, value = parse_key_value(entry, **args) 325 result_dict[key] = value 326 327 return result_dict 328 329 330def key_value_outbuf_to_dict(out_buf, 331 **args): 332 333 r""" 334 Convert a buffer with a key/value string on each line to a dictionary and 335 return it. 336 337 Each line in the out_buf should end with a \n. 338 339 See docstring of parse_key_value function for details on key/value strings. 340 341 Example usage: 342 343 For the following value of out_buf: 344 345 Current Limit State: No Active Power Limit 346 Exception actions: Hard Power Off & Log Event to SEL 347 Power Limit: 0 Watts 348 Correction time: 0 milliseconds 349 Sampling period: 0 seconds 350 351 And the following call in python: 352 353 power_limit = key_value_outbuf_to_dict(out_buf) 354 355 The resulting power_limit directory would look like this: 356 357 power_limit: 358 [current_limit_state]: No Active Power Limit 359 [exception_actions]: Hard Power Off & Log Event to SEL 360 [power_limit]: 0 Watts 361 [correction_time]: 0 milliseconds 362 [sampling_period]: 0 seconds 363 364 Description of argument(s): 365 out_buf A buffer with a key/value string on each 366 line. (See docstring of parse_key_value 367 function for details). 368 **args Arguments to be interpreted by 369 parse_key_value. (See docstring of 370 parse_key_value function for details). 371 """ 372 373 # Create key_var_list and remove null entries. 374 key_var_list = list(filter(None, out_buf.split("\n"))) 375 return key_value_list_to_dict(key_var_list, **args) 376 377 378def list_to_report(report_list, 379 to_lower=1): 380 381 r""" 382 Convert a list containing report text lines to a report "object" and 383 return it. 384 385 The first entry in report_list must be a header line consisting of column 386 names delimited by white space. No column name may contain white space. 387 The remaining report_list entries should contain tabular data which 388 corresponds to the column names. 389 390 A report object is a list where each entry is a dictionary whose keys are 391 the field names from the first entry in report_list. 392 393 Example: 394 Given the following report_list as input: 395 396 rl: 397 rl[0]: Filesystem 1K-blocks Used Available Use% Mounted on 398 rl[1]: dev 247120 0 247120 0% /dev 399 rl[2]: tmpfs 248408 79792 168616 32% /run 400 401 This function will return a list of dictionaries as shown below: 402 403 df_report: 404 df_report[0]: 405 [filesystem]: dev 406 [1k-blocks]: 247120 407 [used]: 0 408 [available]: 247120 409 [use%]: 0% 410 [mounted]: /dev 411 df_report[1]: 412 [filesystem]: dev 413 [1k-blocks]: 247120 414 [used]: 0 415 [available]: 247120 416 [use%]: 0% 417 [mounted]: /dev 418 419 Notice that because "Mounted on" contains a space, "on" would be 420 considered the 7th field. In this case, there is never any data in field 421 7 so things work out nicely. A caller could do some pre-processing if 422 desired (e.g. change "Mounted on" to "Mounted_on"). 423 424 Description of argument(s): 425 report_list A list where each entry is one line of 426 output from a report. The first entry 427 must be a header line which contains 428 column names. Column names may not 429 contain spaces. 430 to_lower Change the resulting key names to lower 431 case. 432 """ 433 434 # Process header line. 435 header_line = report_list[0] 436 if to_lower: 437 header_line = header_line.lower() 438 columns = header_line.split() 439 440 report_obj = [] 441 for report_line in report_list[1:]: 442 line = report_list[1].split() 443 try: 444 line_dict = collections.OrderedDict(zip(columns, line)) 445 except AttributeError: 446 line_dict = DotDict(zip(columns, line)) 447 report_obj.append(line_dict) 448 449 return report_obj 450 451 452def outbuf_to_report(out_buf, 453 **args): 454 455 r""" 456 Convert a text buffer containing report lines to a report "object" and 457 return it. 458 459 Refer to list_to_report (above) for more details. 460 461 Example: 462 463 Given the following out_buf: 464 465 Filesystem 1K-blocks Used Available Use% Mounted on 466 dev 247120 0 247120 0% /dev 467 tmpfs 248408 79792 168616 32% /run 468 469 This function will return a list of dictionaries as shown below: 470 471 df_report: 472 df_report[0]: 473 [filesystem]: dev 474 [1k-blocks]: 247120 475 [used]: 0 476 [available]: 247120 477 [use%]: 0% 478 [mounted]: /dev 479 df_report[1]: 480 [filesystem]: dev 481 [1k-blocks]: 247120 482 [used]: 0 483 [available]: 247120 484 [use%]: 0% 485 [mounted]: /dev 486 487 Other possible uses: 488 - Process the output of a ps command. 489 - Process the output of an ls command (the caller would need to supply 490 column names) 491 492 Description of argument(s): 493 out_buf A text report The first line must be a 494 header line which contains column names. 495 Column names may not contain spaces. 496 **args Arguments to be interpreted by 497 list_to_report. (See docstring of 498 list_to_report function for details). 499 """ 500 501 report_list = filter(None, out_buf.split("\n")) 502 return list_to_report(report_list, **args) 503