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