1#!/usr/bin/env python 2 3r""" 4See class prolog below for details. 5""" 6 7import os 8import re 9import sys 10import yaml 11import json 12import time 13import logging 14import platform 15from errno import EACCES, EPERM 16import subprocess 17from ssh_utility import SSHRemoteclient 18from telnet_utility import TelnetRemoteclient 19 20 21class FFDCCollector: 22 23 r""" 24 Sends commands from configuration file to the targeted system to collect log files. 25 Fetch and store generated files at the specified location. 26 27 """ 28 29 def __init__(self, 30 hostname, 31 username, 32 password, 33 ffdc_config, 34 location, 35 remote_type, 36 remote_protocol, 37 env_vars, 38 econfig, 39 log_level): 40 r""" 41 Description of argument(s): 42 43 hostname name/ip of the targeted (remote) system 44 username user on the targeted system with access to FFDC files 45 password password for user on targeted system 46 ffdc_config configuration file listing commands and files for FFDC 47 location where to store collected FFDC 48 remote_type os type of the remote host 49 remote_protocol Protocol to use to collect data 50 env_vars User define CLI env vars '{"key : "value"}' 51 econfig User define env vars YAML file 52 53 """ 54 55 self.hostname = hostname 56 self.username = username 57 self.password = password 58 self.ffdc_config = ffdc_config 59 self.location = location + "/" + remote_type.upper() 60 self.ssh_remoteclient = None 61 self.telnet_remoteclient = None 62 self.ffdc_dir_path = "" 63 self.ffdc_prefix = "" 64 self.target_type = remote_type.upper() 65 self.remote_protocol = remote_protocol.upper() 66 self.env_vars = env_vars 67 self.econfig = econfig 68 self.start_time = 0 69 self.elapsed_time = '' 70 self.logger = None 71 72 # Set prefix values for scp files and directory. 73 # Since the time stamp is at second granularity, these values are set here 74 # to be sure that all files for this run will have same timestamps 75 # and they will be saved in the same directory. 76 # self.location == local system for now 77 self.set_ffdc_defaults() 78 79 # Logger for this run. Need to be after set_ffdc_defaults() 80 self.script_logging(getattr(logging, log_level.upper())) 81 82 # Verify top level directory exists for storage 83 self.validate_local_store(self.location) 84 85 if self.verify_script_env(): 86 # Load default or user define YAML configuration file. 87 with open(self.ffdc_config, 'r') as file: 88 self.ffdc_actions = yaml.load(file, Loader=yaml.FullLoader) 89 90 if self.target_type not in self.ffdc_actions.keys(): 91 self.logger.error( 92 "\n\tERROR: %s is not listed in %s.\n\n" % (self.target_type, self.ffdc_config)) 93 sys.exit(-1) 94 else: 95 sys.exit(-1) 96 97 # Load ENV vars from user. 98 self.logger.info("\n\tENV: User define input YAML variables") 99 self.env_dict = {} 100 self. load_env() 101 102 def verify_script_env(self): 103 104 # Import to log version 105 import click 106 import paramiko 107 108 run_env_ok = True 109 110 redfishtool_version = self.run_tool_cmd('redfishtool -V').split(' ')[2].strip('\n') 111 ipmitool_version = self.run_tool_cmd('ipmitool -V').split(' ')[2] 112 113 self.logger.info("\n\t---- Script host environment ----") 114 self.logger.info("\t{:<10} {:<10}".format('Script hostname', os.uname()[1])) 115 self.logger.info("\t{:<10} {:<10}".format('Script host os', platform.platform())) 116 self.logger.info("\t{:<10} {:>10}".format('Python', platform.python_version())) 117 self.logger.info("\t{:<10} {:>10}".format('PyYAML', yaml.__version__)) 118 self.logger.info("\t{:<10} {:>10}".format('click', click.__version__)) 119 self.logger.info("\t{:<10} {:>10}".format('paramiko', paramiko.__version__)) 120 self.logger.info("\t{:<10} {:>9}".format('redfishtool', redfishtool_version)) 121 self.logger.info("\t{:<10} {:>12}".format('ipmitool', ipmitool_version)) 122 123 if eval(yaml.__version__.replace('.', ',')) < (5, 4, 1): 124 self.logger.error("\n\tERROR: Python or python packages do not meet minimum version requirement.") 125 self.logger.error("\tERROR: PyYAML version 5.4.1 or higher is needed.\n") 126 run_env_ok = False 127 128 self.logger.info("\t---- End script host environment ----") 129 return run_env_ok 130 131 def script_logging(self, 132 log_level_attr): 133 r""" 134 Create logger 135 136 """ 137 self.logger = logging.getLogger() 138 self.logger.setLevel(log_level_attr) 139 log_file_handler = logging.FileHandler(self.ffdc_dir_path + "collector.log") 140 141 stdout_handler = logging.StreamHandler(sys.stdout) 142 self.logger.addHandler(log_file_handler) 143 self.logger.addHandler(stdout_handler) 144 145 # Turn off paramiko INFO logging 146 logging.getLogger("paramiko").setLevel(logging.WARNING) 147 148 def target_is_pingable(self): 149 r""" 150 Check if target system is ping-able. 151 152 """ 153 response = os.system("ping -c 1 %s 2>&1 >/dev/null" % self.hostname) 154 if response == 0: 155 self.logger.info("\n\t[Check] %s is ping-able.\t\t [OK]" % self.hostname) 156 return True 157 else: 158 self.logger.error( 159 "\n\tERROR: %s is not ping-able. FFDC collection aborted.\n" % self.hostname) 160 sys.exit(-1) 161 162 def collect_ffdc(self): 163 r""" 164 Initiate FFDC Collection depending on requested protocol. 165 166 """ 167 168 self.logger.info("\n\t---- Start communicating with %s ----" % self.hostname) 169 self.start_time = time.time() 170 171 # Find the list of target and protocol supported. 172 check_protocol_list = [] 173 config_dict = self.ffdc_actions 174 175 for target_type in config_dict.keys(): 176 if self.target_type != target_type: 177 continue 178 179 for k, v in config_dict[target_type].items(): 180 if config_dict[target_type][k]['PROTOCOL'][0] not in check_protocol_list: 181 check_protocol_list.append(config_dict[target_type][k]['PROTOCOL'][0]) 182 183 self.logger.info("\n\t %s protocol type: %s" % (self.target_type, check_protocol_list)) 184 185 verified_working_protocol = self.verify_protocol(check_protocol_list) 186 187 if verified_working_protocol: 188 self.logger.info("\n\t---- Completed protocol pre-requisite check ----\n") 189 190 # Verify top level directory exists for storage 191 self.validate_local_store(self.location) 192 193 if ((self.remote_protocol not in verified_working_protocol) and (self.remote_protocol != 'ALL')): 194 self.logger.info("\n\tWorking protocol list: %s" % verified_working_protocol) 195 self.logger.error( 196 '\tERROR: Requested protocol %s is not in working protocol list.\n' 197 % self.remote_protocol) 198 sys.exit(-1) 199 else: 200 self.generate_ffdc(verified_working_protocol) 201 202 def ssh_to_target_system(self): 203 r""" 204 Open a ssh connection to targeted system. 205 206 """ 207 208 self.ssh_remoteclient = SSHRemoteclient(self.hostname, 209 self.username, 210 self.password) 211 212 if self.ssh_remoteclient.ssh_remoteclient_login(): 213 self.logger.info("\n\t[Check] %s SSH connection established.\t [OK]" % self.hostname) 214 215 # Check scp connection. 216 # If scp connection fails, 217 # continue with FFDC generation but skip scp files to local host. 218 self.ssh_remoteclient.scp_connection() 219 return True 220 else: 221 self.logger.info("\n\t[Check] %s SSH connection.\t [NOT AVAILABLE]" % self.hostname) 222 return False 223 224 def telnet_to_target_system(self): 225 r""" 226 Open a telnet connection to targeted system. 227 """ 228 self.telnet_remoteclient = TelnetRemoteclient(self.hostname, 229 self.username, 230 self.password) 231 if self.telnet_remoteclient.tn_remoteclient_login(): 232 self.logger.info("\n\t[Check] %s Telnet connection established.\t [OK]" % self.hostname) 233 return True 234 else: 235 self.logger.info("\n\t[Check] %s Telnet connection.\t [NOT AVAILABLE]" % self.hostname) 236 return False 237 238 def generate_ffdc(self, working_protocol_list): 239 r""" 240 Determine actions based on remote host type 241 242 Description of argument(s): 243 working_protocol_list list of confirmed working protocols to connect to remote host. 244 """ 245 246 self.logger.info("\n\t---- Executing commands on " + self.hostname + " ----") 247 self.logger.info("\n\tWorking protocol list: %s" % working_protocol_list) 248 249 config_dict = self.ffdc_actions 250 for target_type in config_dict.keys(): 251 if self.target_type != target_type: 252 continue 253 254 self.logger.info("\n\tFFDC Path: %s " % self.ffdc_dir_path) 255 self.logger.info("\tSystem Type: %s" % target_type) 256 for k, v in config_dict[target_type].items(): 257 258 if self.remote_protocol not in working_protocol_list \ 259 and self.remote_protocol != 'ALL': 260 continue 261 262 protocol = config_dict[target_type][k]['PROTOCOL'][0] 263 264 if protocol not in working_protocol_list: 265 continue 266 267 if protocol in working_protocol_list: 268 if protocol == 'SSH' or protocol == 'SCP': 269 self.protocol_ssh(protocol, target_type, k) 270 elif protocol == 'TELNET': 271 self.protocol_telnet(target_type, k) 272 elif protocol == 'REDFISH' or protocol == 'IPMI' or protocol == 'SHELL': 273 self.protocol_execute(protocol, target_type, k) 274 else: 275 self.logger.error("\n\tERROR: %s is not available for %s." % (protocol, self.hostname)) 276 277 # Close network connection after collecting all files 278 self.elapsed_time = time.strftime("%H:%M:%S", time.gmtime(time.time() - self.start_time)) 279 if self.ssh_remoteclient: 280 self.ssh_remoteclient.ssh_remoteclient_disconnect() 281 if self.telnet_remoteclient: 282 self.telnet_remoteclient.tn_remoteclient_disconnect() 283 284 def protocol_ssh(self, 285 protocol, 286 target_type, 287 sub_type): 288 r""" 289 Perform actions using SSH and SCP protocols. 290 291 Description of argument(s): 292 protocol Protocol to execute. 293 target_type OS Type of remote host. 294 sub_type Group type of commands. 295 """ 296 297 if protocol == 'SCP': 298 self.group_copy(self.ffdc_actions[target_type][sub_type]) 299 else: 300 self.collect_and_copy_ffdc(self.ffdc_actions[target_type][sub_type]) 301 302 def protocol_telnet(self, 303 target_type, 304 sub_type): 305 r""" 306 Perform actions using telnet protocol. 307 Description of argument(s): 308 target_type OS Type of remote host. 309 """ 310 self.logger.info("\n\t[Run] Executing commands on %s using %s" % (self.hostname, 'TELNET')) 311 telnet_files_saved = [] 312 progress_counter = 0 313 list_of_commands = self.ffdc_actions[target_type][sub_type]['COMMANDS'] 314 for index, each_cmd in enumerate(list_of_commands, start=0): 315 command_txt, command_timeout = self.unpack_command(each_cmd) 316 result = self.telnet_remoteclient.execute_command(command_txt, command_timeout) 317 if result: 318 try: 319 targ_file = self.ffdc_actions[target_type][sub_type]['FILES'][index] 320 except IndexError: 321 targ_file = command_txt 322 self.logger.warning( 323 "\n\t[WARN] Missing filename to store data from telnet %s." % each_cmd) 324 self.logger.warning("\t[WARN] Data will be stored in %s." % targ_file) 325 targ_file_with_path = (self.ffdc_dir_path 326 + self.ffdc_prefix 327 + targ_file) 328 # Creates a new file 329 with open(targ_file_with_path, 'wb') as fp: 330 fp.write(result) 331 fp.close 332 telnet_files_saved.append(targ_file) 333 progress_counter += 1 334 self.print_progress(progress_counter) 335 self.logger.info("\n\t[Run] Commands execution completed.\t\t [OK]") 336 for file in telnet_files_saved: 337 self.logger.info("\n\t\tSuccessfully save file " + file + ".") 338 339 def protocol_execute(self, 340 protocol, 341 target_type, 342 sub_type): 343 r""" 344 Perform actions for a given protocol. 345 346 Description of argument(s): 347 protocol Protocol to execute. 348 target_type OS Type of remote host. 349 sub_type Group type of commands. 350 """ 351 352 self.logger.info("\n\t[Run] Executing commands to %s using %s" % (self.hostname, protocol)) 353 executed_files_saved = [] 354 progress_counter = 0 355 list_of_cmd = self.get_command_list(self.ffdc_actions[target_type][sub_type]) 356 for index, each_cmd in enumerate(list_of_cmd, start=0): 357 result = self.run_tool_cmd(each_cmd) 358 if result: 359 try: 360 targ_file = self.get_file_list(self.ffdc_actions[target_type][sub_type])[index] 361 except IndexError: 362 targ_file = each_cmd.split('/')[-1] 363 self.logger.warning( 364 "\n\t[WARN] Missing filename to store data from %s." % each_cmd) 365 self.logger.warning("\t[WARN] Data will be stored in %s." % targ_file) 366 367 targ_file_with_path = (self.ffdc_dir_path 368 + self.ffdc_prefix 369 + targ_file) 370 371 # Creates a new file 372 with open(targ_file_with_path, 'w') as fp: 373 fp.write(result) 374 fp.close 375 executed_files_saved.append(targ_file) 376 377 progress_counter += 1 378 self.print_progress(progress_counter) 379 380 self.logger.info("\n\t[Run] Commands execution completed.\t\t [OK]") 381 382 for file in executed_files_saved: 383 self.logger.info("\n\t\tSuccessfully save file " + file + ".") 384 385 def collect_and_copy_ffdc(self, 386 ffdc_actions_for_target_type, 387 form_filename=False): 388 r""" 389 Send commands in ffdc_config file to targeted system. 390 391 Description of argument(s): 392 ffdc_actions_for_target_type commands and files for the selected remote host type. 393 form_filename if true, pre-pend self.target_type to filename 394 """ 395 396 # Executing commands, if any 397 self.ssh_execute_ffdc_commands(ffdc_actions_for_target_type, 398 form_filename) 399 400 # Copying files 401 if self.ssh_remoteclient.scpclient: 402 self.logger.info("\n\n\tCopying FFDC files from remote system %s.\n" % self.hostname) 403 404 # Retrieving files from target system 405 list_of_files = self.get_file_list(ffdc_actions_for_target_type) 406 self.scp_ffdc(self.ffdc_dir_path, self.ffdc_prefix, form_filename, list_of_files) 407 else: 408 self.logger.info("\n\n\tSkip copying FFDC files from remote system %s.\n" % self.hostname) 409 410 def get_command_list(self, 411 ffdc_actions_for_target_type): 412 r""" 413 Fetch list of commands from configuration file 414 415 Description of argument(s): 416 ffdc_actions_for_target_type commands and files for the selected remote host type. 417 """ 418 try: 419 list_of_commands = ffdc_actions_for_target_type['COMMANDS'] 420 except KeyError: 421 list_of_commands = [] 422 return list_of_commands 423 424 def get_file_list(self, 425 ffdc_actions_for_target_type): 426 r""" 427 Fetch list of commands from configuration file 428 429 Description of argument(s): 430 ffdc_actions_for_target_type commands and files for the selected remote host type. 431 """ 432 try: 433 list_of_files = ffdc_actions_for_target_type['FILES'] 434 except KeyError: 435 list_of_files = [] 436 return list_of_files 437 438 def unpack_command(self, 439 command): 440 r""" 441 Unpack command from config file 442 443 Description of argument(s): 444 command Command from config file. 445 """ 446 if isinstance(command, dict): 447 command_txt = next(iter(command)) 448 command_timeout = next(iter(command.values())) 449 elif isinstance(command, str): 450 command_txt = command 451 # Default command timeout 60 seconds 452 command_timeout = 60 453 454 return command_txt, command_timeout 455 456 def ssh_execute_ffdc_commands(self, 457 ffdc_actions_for_target_type, 458 form_filename=False): 459 r""" 460 Send commands in ffdc_config file to targeted system. 461 462 Description of argument(s): 463 ffdc_actions_for_target_type commands and files for the selected remote host type. 464 form_filename if true, pre-pend self.target_type to filename 465 """ 466 self.logger.info("\n\t[Run] Executing commands on %s using %s" 467 % (self.hostname, ffdc_actions_for_target_type['PROTOCOL'][0])) 468 469 list_of_commands = self.get_command_list(ffdc_actions_for_target_type) 470 # If command list is empty, returns 471 if not list_of_commands: 472 return 473 474 progress_counter = 0 475 for command in list_of_commands: 476 command_txt, command_timeout = self.unpack_command(command) 477 478 if form_filename: 479 command_txt = str(command_txt % self.target_type) 480 481 cmd_exit_code, err, response = \ 482 self.ssh_remoteclient.execute_command(command_txt, command_timeout) 483 484 if cmd_exit_code: 485 self.logger.warning( 486 "\n\t\t[WARN] %s exits with code %s." % (command_txt, str(cmd_exit_code))) 487 self.logger.warning("\t\t[WARN] %s " % err) 488 489 progress_counter += 1 490 self.print_progress(progress_counter) 491 492 self.logger.info("\n\t[Run] Commands execution completed.\t\t [OK]") 493 494 def group_copy(self, 495 ffdc_actions_for_target_type): 496 r""" 497 scp group of files (wild card) from remote host. 498 499 Description of argument(s): 500 fdc_actions_for_target_type commands and files for the selected remote host type. 501 """ 502 503 if self.ssh_remoteclient.scpclient: 504 self.logger.info("\n\tCopying files from remote system %s via SCP.\n" % self.hostname) 505 506 list_of_commands = self.get_command_list(ffdc_actions_for_target_type) 507 # If command list is empty, returns 508 if not list_of_commands: 509 return 510 511 for command in list_of_commands: 512 try: 513 filename = command.split(' ')[2] 514 except IndexError: 515 self.logger.info("\t\tInvalid command %s" % command) 516 continue 517 518 cmd_exit_code, err, response = \ 519 self.ssh_remoteclient.execute_command(command) 520 521 # If file does not exist, code take no action. 522 # cmd_exit_code is ignored for this scenario. 523 if response: 524 scp_result = self.ssh_remoteclient.scp_file_from_remote(filename, self.ffdc_dir_path) 525 if scp_result: 526 self.logger.info("\t\tSuccessfully copied from " + self.hostname + ':' + filename) 527 else: 528 self.logger.info("\t\tThere is no " + filename) 529 530 else: 531 self.logger.info("\n\n\tSkip copying files from remote system %s.\n" % self.hostname) 532 533 def scp_ffdc(self, 534 targ_dir_path, 535 targ_file_prefix, 536 form_filename, 537 file_list=None, 538 quiet=None): 539 r""" 540 SCP all files in file_dict to the indicated directory on the local system. 541 542 Description of argument(s): 543 targ_dir_path The path of the directory to receive the files. 544 targ_file_prefix Prefix which will be pre-pended to each 545 target file's name. 546 file_dict A dictionary of files to scp from targeted system to this system 547 548 """ 549 550 progress_counter = 0 551 for filename in file_list: 552 if form_filename: 553 filename = str(filename % self.target_type) 554 source_file_path = filename 555 targ_file_path = targ_dir_path + targ_file_prefix + filename.split('/')[-1] 556 557 # If source file name contains wild card, copy filename as is. 558 if '*' in source_file_path: 559 scp_result = self.ssh_remoteclient.scp_file_from_remote(source_file_path, self.ffdc_dir_path) 560 else: 561 scp_result = self.ssh_remoteclient.scp_file_from_remote(source_file_path, targ_file_path) 562 563 if not quiet: 564 if scp_result: 565 self.logger.info( 566 "\t\tSuccessfully copied from " + self.hostname + ':' + source_file_path + ".\n") 567 else: 568 self.logger.info( 569 "\t\tFail to copy from " + self.hostname + ':' + source_file_path + ".\n") 570 else: 571 progress_counter += 1 572 self.print_progress(progress_counter) 573 574 def set_ffdc_defaults(self): 575 r""" 576 Set a default value for self.ffdc_dir_path and self.ffdc_prefix. 577 Collected ffdc file will be stored in dir /self.location/hostname_timestr/. 578 Individual ffdc file will have timestr_filename. 579 580 Description of class variables: 581 self.ffdc_dir_path The dir path where collected ffdc data files should be put. 582 583 self.ffdc_prefix The prefix to be given to each ffdc file name. 584 585 """ 586 587 timestr = time.strftime("%Y%m%d-%H%M%S") 588 self.ffdc_dir_path = self.location + "/" + self.hostname + "_" + timestr + "/" 589 self.ffdc_prefix = timestr + "_" 590 self.validate_local_store(self.ffdc_dir_path) 591 592 def validate_local_store(self, dir_path): 593 r""" 594 Ensure path exists to store FFDC files locally. 595 596 Description of variable: 597 dir_path The dir path where collected ffdc data files will be stored. 598 599 """ 600 601 if not os.path.exists(dir_path): 602 try: 603 os.makedirs(dir_path, 0o755) 604 except (IOError, OSError) as e: 605 # PermissionError 606 if e.errno == EPERM or e.errno == EACCES: 607 self.logger.error( 608 '\tERROR: os.makedirs %s failed with PermissionError.\n' % dir_path) 609 else: 610 self.logger.error( 611 '\tERROR: os.makedirs %s failed with %s.\n' % (dir_path, e.strerror)) 612 sys.exit(-1) 613 614 def print_progress(self, progress): 615 r""" 616 Print activity progress + 617 618 Description of variable: 619 progress Progress counter. 620 621 """ 622 623 sys.stdout.write("\r\t" + "+" * progress) 624 sys.stdout.flush() 625 time.sleep(.1) 626 627 def verify_redfish(self): 628 r""" 629 Verify remote host has redfish service active 630 631 """ 632 redfish_parm = 'redfishtool -r ' \ 633 + self.hostname + ' -S Always raw GET /redfish/v1/' 634 return(self.run_tool_cmd(redfish_parm, True)) 635 636 def verify_ipmi(self): 637 r""" 638 Verify remote host has IPMI LAN service active 639 640 """ 641 if self.target_type == 'OPENBMC': 642 ipmi_parm = 'ipmitool -I lanplus -C 17 -U ' + self.username + ' -P ' \ 643 + self.password + ' -H ' + self.hostname + ' power status' 644 else: 645 ipmi_parm = 'ipmitool -I lanplus -P ' \ 646 + self.password + ' -H ' + self.hostname + ' power status' 647 648 return(self.run_tool_cmd(ipmi_parm, True)) 649 650 def run_tool_cmd(self, 651 parms_string, 652 quiet=False): 653 r""" 654 Run CLI standard tool or scripts. 655 656 Description of variable: 657 parms_string tool command options. 658 quiet do not print tool error message if True 659 """ 660 661 result = subprocess.run([parms_string], 662 stdout=subprocess.PIPE, 663 stderr=subprocess.PIPE, 664 shell=True, 665 universal_newlines=True) 666 667 if result.stderr and not quiet: 668 self.logger.error('\n\t\tERROR with %s ' % parms_string) 669 self.logger.error('\t\t' + result.stderr) 670 671 return result.stdout 672 673 def verify_protocol(self, protocol_list): 674 r""" 675 Perform protocol working check. 676 677 Description of argument(s): 678 protocol_list List of protocol. 679 """ 680 681 tmp_list = [] 682 if self.target_is_pingable(): 683 tmp_list.append("SHELL") 684 685 for protocol in protocol_list: 686 if self.remote_protocol != 'ALL': 687 if self.remote_protocol != protocol: 688 continue 689 690 # Only check SSH/SCP once for both protocols 691 if protocol == 'SSH' or protocol == 'SCP' and protocol not in tmp_list: 692 if self.ssh_to_target_system(): 693 # Add only what user asked. 694 if self.remote_protocol != 'ALL': 695 tmp_list.append(self.remote_protocol) 696 else: 697 tmp_list.append('SSH') 698 tmp_list.append('SCP') 699 700 if protocol == 'TELNET': 701 if self.telnet_to_target_system(): 702 tmp_list.append(protocol) 703 704 if protocol == 'REDFISH': 705 if self.verify_redfish(): 706 tmp_list.append(protocol) 707 self.logger.info("\n\t[Check] %s Redfish Service.\t\t [OK]" % self.hostname) 708 else: 709 self.logger.info("\n\t[Check] %s Redfish Service.\t\t [NOT AVAILABLE]" % self.hostname) 710 711 if protocol == 'IPMI': 712 if self.verify_ipmi(): 713 tmp_list.append(protocol) 714 self.logger.info("\n\t[Check] %s IPMI LAN Service.\t\t [OK]" % self.hostname) 715 else: 716 self.logger.info("\n\t[Check] %s IPMI LAN Service.\t\t [NOT AVAILABLE]" % self.hostname) 717 718 return tmp_list 719 720 def load_env(self): 721 r""" 722 Perform protocol working check. 723 724 """ 725 # This is for the env vars a user can use in YAML to load it at runtime. 726 # Example YAML: 727 # -COMMANDS: 728 # - my_command ${hostname} ${username} ${password} 729 os.environ['hostname'] = self.hostname 730 os.environ['username'] = self.username 731 os.environ['password'] = self.password 732 733 # Append default Env. 734 self.env_dict['hostname'] = self.hostname 735 self.env_dict['username'] = self.username 736 self.env_dict['password'] = self.password 737 738 try: 739 tmp_env_dict = {} 740 if self.env_vars: 741 tmp_env_dict = json.loads(self.env_vars) 742 # Export ENV vars default. 743 for key, value in tmp_env_dict.items(): 744 os.environ[key] = value 745 self.env_dict[key] = str(value) 746 747 if self.econfig: 748 with open(self.econfig, 'r') as file: 749 tmp_env_dict = yaml.load(file, Loader=yaml.FullLoader) 750 # Export ENV vars. 751 for key, value in tmp_env_dict['env_params'].items(): 752 os.environ[key] = str(value) 753 self.env_dict[key] = str(value) 754 except json.decoder.JSONDecodeError as e: 755 self.logger.error("\n\tERROR: %s " % e) 756 sys.exit(-1) 757 758 # This to mask the password from displaying on the console. 759 mask_dict = self.env_dict.copy() 760 for k, v in mask_dict.items(): 761 if k.lower().find("password") != -1: 762 hidden_text = [] 763 hidden_text.append(v) 764 password_regex = '(' +\ 765 '|'.join([re.escape(x) for x in hidden_text]) + ')' 766 mask_dict[k] = re.sub(password_regex, "********", v) 767 768 self.logger.info(json.dumps(mask_dict, indent=8, sort_keys=False)) 769