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