blob: c717e2dbfdaa417879affce7308f661f049251a8 [file] [log] [blame]
Peter D Phan72ce6b82021-06-03 06:18:26 -05001#!/usr/bin/env python
2
3r"""
4See class prolog below for details.
5"""
6
7import os
8import sys
9import yaml
George Keishing4885b2f2021-07-21 15:22:45 -050010import json
Peter D Phan72ce6b82021-06-03 06:18:26 -050011import time
Peter D Phane86d9a52021-07-15 10:42:25 -050012import logging
Peter D Phan72ce6b82021-06-03 06:18:26 -050013import platform
14from errno import EACCES, EPERM
Peter D Phan0c669772021-06-24 13:52:42 -050015import subprocess
Peter D Phan72ce6b82021-06-03 06:18:26 -050016from ssh_utility import SSHRemoteclient
Peter D Phan5963d632021-07-12 09:58:55 -050017from telnet_utility import TelnetRemoteclient
Peter D Phan72ce6b82021-06-03 06:18:26 -050018
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
Peter D Phan0c669772021-06-24 13:52:42 -050028 def __init__(self,
29 hostname,
30 username,
31 password,
32 ffdc_config,
33 location,
34 remote_type,
Peter D Phane86d9a52021-07-15 10:42:25 -050035 remote_protocol,
George Keishing4885b2f2021-07-21 15:22:45 -050036 env_vars,
Peter D Phane86d9a52021-07-15 10:42:25 -050037 log_level):
Peter D Phan72ce6b82021-06-03 06:18:26 -050038 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
Peter D Phan04aca3b2021-06-21 10:37:18 -050045 location where to store collected FFDC
46 remote_type os type of the remote host
Peter D Phan72ce6b82021-06-03 06:18:26 -050047
48 """
Peter D Phane86d9a52021-07-15 10:42:25 -050049
50 self.hostname = hostname
51 self.username = username
52 self.password = password
George Keishing04d29102021-07-16 02:05:57 -050053 # 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
Peter D Phane86d9a52021-07-15 10:42:25 -050061 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
Peter D Phan72ce6b82021-06-03 06:18:26 -050086 if self.verify_script_env():
Peter D Phane86d9a52021-07-15 10:42:25 -050087 # 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)
Peter D Phan72ce6b82021-06-03 06:18:26 -050095 else:
Peter D Phan8462faf2021-06-16 12:24:15 -050096 sys.exit(-1)
Peter D Phan72ce6b82021-06-03 06:18:26 -050097
George Keishing4885b2f2021-07-21 15:22:45 -050098 # Load ENV vars from user.
99 try:
100 if env_vars:
101 env_dict = json.loads(env_vars)
102 self.logger.info("\n\tENV: User define input YAML variables")
103 self.logger.info(json.dumps(env_dict, indent=8, sort_keys=True))
104
105 # Export ENV vars default.
106 for key, value in env_dict.items():
107 os.environ[key] = value
108 except json.decoder.JSONDecodeError as e:
109 self.logger.error("\n\tERROR: %s " % e)
110 sys.exit(-1)
111
Peter D Phan72ce6b82021-06-03 06:18:26 -0500112 def verify_script_env(self):
113
114 # Import to log version
115 import click
116 import paramiko
117
118 run_env_ok = True
Peter D Phan0c669772021-06-24 13:52:42 -0500119
George Keishingeafba182021-06-29 13:44:58 -0500120 redfishtool_version = self.run_redfishtool('-V').split(' ')[2].strip('\n')
121 ipmitool_version = self.run_ipmitool('-V').split(' ')[2]
Peter D Phan0c669772021-06-24 13:52:42 -0500122
Peter D Phane86d9a52021-07-15 10:42:25 -0500123 self.logger.info("\n\t---- Script host environment ----")
124 self.logger.info("\t{:<10} {:<10}".format('Script hostname', os.uname()[1]))
125 self.logger.info("\t{:<10} {:<10}".format('Script host os', platform.platform()))
126 self.logger.info("\t{:<10} {:>10}".format('Python', platform.python_version()))
127 self.logger.info("\t{:<10} {:>10}".format('PyYAML', yaml.__version__))
128 self.logger.info("\t{:<10} {:>10}".format('click', click.__version__))
129 self.logger.info("\t{:<10} {:>10}".format('paramiko', paramiko.__version__))
130 self.logger.info("\t{:<10} {:>9}".format('redfishtool', redfishtool_version))
131 self.logger.info("\t{:<10} {:>12}".format('ipmitool', ipmitool_version))
Peter D Phan72ce6b82021-06-03 06:18:26 -0500132
Peter D Phan8462faf2021-06-16 12:24:15 -0500133 if eval(yaml.__version__.replace('.', ',')) < (5, 4, 1):
Peter D Phane86d9a52021-07-15 10:42:25 -0500134 self.logger.error("\n\tERROR: Python or python packages do not meet minimum version requirement.")
135 self.logger.error("\tERROR: PyYAML version 5.4.1 or higher is needed.\n")
Peter D Phan72ce6b82021-06-03 06:18:26 -0500136 run_env_ok = False
137
Peter D Phane86d9a52021-07-15 10:42:25 -0500138 self.logger.info("\t---- End script host environment ----")
Peter D Phan72ce6b82021-06-03 06:18:26 -0500139 return run_env_ok
140
Peter D Phane86d9a52021-07-15 10:42:25 -0500141 def script_logging(self,
142 log_level_attr):
143 r"""
144 Create logger
145
146 """
147 self.logger = logging.getLogger()
148 self.logger.setLevel(log_level_attr)
149 log_file_handler = logging.FileHandler(self.ffdc_dir_path + "collector.log")
150
151 stdout_handler = logging.StreamHandler(sys.stdout)
152 self.logger.addHandler(log_file_handler)
153 self.logger.addHandler(stdout_handler)
154
155 # Turn off paramiko INFO logging
156 logging.getLogger("paramiko").setLevel(logging.WARNING)
157
Peter D Phan72ce6b82021-06-03 06:18:26 -0500158 def target_is_pingable(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500159 r"""
160 Check if target system is ping-able.
161
162 """
George Keishing0662e942021-07-13 05:12:20 -0500163 response = os.system("ping -c 1 %s 2>&1 >/dev/null" % self.hostname)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500164 if response == 0:
Peter D Phane86d9a52021-07-15 10:42:25 -0500165 self.logger.info("\n\t[Check] %s is ping-able.\t\t [OK]" % self.hostname)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500166 return True
167 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500168 self.logger.error(
169 "\n>>>>>\tERROR: %s is not ping-able. FFDC collection aborted.\n" % self.hostname)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500170 sys.exit(-1)
171
Peter D Phan72ce6b82021-06-03 06:18:26 -0500172 def collect_ffdc(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500173 r"""
174 Initiate FFDC Collection depending on requested protocol.
175
176 """
177
Peter D Phane86d9a52021-07-15 10:42:25 -0500178 self.logger.info("\n\t---- Start communicating with %s ----" % self.hostname)
Peter D Phan7610bc42021-07-06 06:31:05 -0500179 self.start_time = time.time()
George Keishing772c9772021-06-16 23:23:42 -0500180 working_protocol_list = []
Peter D Phan72ce6b82021-06-03 06:18:26 -0500181 if self.target_is_pingable():
George Keishing04d29102021-07-16 02:05:57 -0500182 working_protocol_list.append("SHELL")
George Keishing772c9772021-06-16 23:23:42 -0500183 # Check supported protocol ping,ssh, redfish are working.
184 if self.ssh_to_target_system():
185 working_protocol_list.append("SSH")
George Keishing615fe322021-06-24 01:24:36 -0500186 working_protocol_list.append("SCP")
Peter D Phan0c669772021-06-24 13:52:42 -0500187
188 # Redfish
189 if self.verify_redfish():
190 working_protocol_list.append("REDFISH")
Peter D Phane86d9a52021-07-15 10:42:25 -0500191 self.logger.info("\n\t[Check] %s Redfish Service.\t\t [OK]" % self.hostname)
Peter D Phan0c669772021-06-24 13:52:42 -0500192 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500193 self.logger.info("\n\t[Check] %s Redfish Service.\t\t [NOT AVAILABLE]" % self.hostname)
Peter D Phan0c669772021-06-24 13:52:42 -0500194
Peter D Phan5963d632021-07-12 09:58:55 -0500195 # IPMI
George Keishingeafba182021-06-29 13:44:58 -0500196 if self.verify_ipmi():
197 working_protocol_list.append("IPMI")
Peter D Phane86d9a52021-07-15 10:42:25 -0500198 self.logger.info("\n\t[Check] %s IPMI LAN Service.\t\t [OK]" % self.hostname)
George Keishingeafba182021-06-29 13:44:58 -0500199 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500200 self.logger.info("\n\t[Check] %s IPMI LAN Service.\t\t [NOT AVAILABLE]" % self.hostname)
George Keishingeafba182021-06-29 13:44:58 -0500201
Peter D Phan5963d632021-07-12 09:58:55 -0500202 # Telnet
203 if self.telnet_to_target_system():
204 working_protocol_list.append("TELNET")
205
Peter D Phan72ce6b82021-06-03 06:18:26 -0500206 # Verify top level directory exists for storage
207 self.validate_local_store(self.location)
Peter D Phane86d9a52021-07-15 10:42:25 -0500208 self.logger.info("\n\t---- Completed protocol pre-requisite check ----\n")
Peter D Phan0c669772021-06-24 13:52:42 -0500209
210 if ((self.remote_protocol not in working_protocol_list) and (self.remote_protocol != 'ALL')):
Peter D Phane86d9a52021-07-15 10:42:25 -0500211 self.logger.info("\n\tWorking protocol list: %s" % working_protocol_list)
212 self.logger.error(
Peter D Phan0c669772021-06-24 13:52:42 -0500213 '>>>>>\tERROR: Requested protocol %s is not in working protocol list.\n'
214 % self.remote_protocol)
215 sys.exit(-1)
216 else:
217 self.generate_ffdc(working_protocol_list)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500218
219 def ssh_to_target_system(self):
220 r"""
221 Open a ssh connection to targeted system.
222
223 """
224
Peter D Phan5963d632021-07-12 09:58:55 -0500225 self.ssh_remoteclient = SSHRemoteclient(self.hostname,
226 self.username,
227 self.password)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500228
Peter D Phan5963d632021-07-12 09:58:55 -0500229 if self.ssh_remoteclient.ssh_remoteclient_login():
Peter D Phane86d9a52021-07-15 10:42:25 -0500230 self.logger.info("\n\t[Check] %s SSH connection established.\t [OK]" % self.hostname)
Peter D Phan733df632021-06-17 13:13:36 -0500231
Peter D Phan5963d632021-07-12 09:58:55 -0500232 # Check scp connection.
233 # If scp connection fails,
234 # continue with FFDC generation but skip scp files to local host.
235 self.ssh_remoteclient.scp_connection()
236 return True
237 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500238 self.logger.info("\n\t[Check] %s SSH connection.\t [NOT AVAILABLE]" % self.hostname)
Peter D Phan5963d632021-07-12 09:58:55 -0500239 return False
240
241 def telnet_to_target_system(self):
242 r"""
243 Open a telnet connection to targeted system.
244 """
245 self.telnet_remoteclient = TelnetRemoteclient(self.hostname,
246 self.username,
247 self.password)
248 if self.telnet_remoteclient.tn_remoteclient_login():
Peter D Phane86d9a52021-07-15 10:42:25 -0500249 self.logger.info("\n\t[Check] %s Telnet connection established.\t [OK]" % self.hostname)
Peter D Phan5963d632021-07-12 09:58:55 -0500250 return True
251 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500252 self.logger.info("\n\t[Check] %s Telnet connection.\t [NOT AVAILABLE]" % self.hostname)
Peter D Phan5963d632021-07-12 09:58:55 -0500253 return False
Peter D Phan72ce6b82021-06-03 06:18:26 -0500254
George Keishing772c9772021-06-16 23:23:42 -0500255 def generate_ffdc(self, working_protocol_list):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500256 r"""
Peter D Phan04aca3b2021-06-21 10:37:18 -0500257 Determine actions based on remote host type
Peter D Phan72ce6b82021-06-03 06:18:26 -0500258
Peter D Phan04aca3b2021-06-21 10:37:18 -0500259 Description of argument(s):
260 working_protocol_list list of confirmed working protocols to connect to remote host.
Peter D Phan72ce6b82021-06-03 06:18:26 -0500261 """
262
Peter D Phane86d9a52021-07-15 10:42:25 -0500263 self.logger.info("\n\t---- Executing commands on " + self.hostname + " ----")
264 self.logger.info("\n\tWorking protocol list: %s" % working_protocol_list)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500265
George Keishing86764b52021-07-01 04:32:03 -0500266 ffdc_actions = self.ffdc_actions
Peter D Phan2b8052d2021-06-22 10:55:41 -0500267
Peter D Phan72ce6b82021-06-03 06:18:26 -0500268 for machine_type in ffdc_actions.keys():
George Keishing6ea92b02021-07-01 11:20:50 -0500269 if self.target_type != machine_type:
270 continue
Peter D Phan72ce6b82021-06-03 06:18:26 -0500271
Peter D Phane86d9a52021-07-15 10:42:25 -0500272 self.logger.info("\n\tFFDC Path: %s " % self.ffdc_dir_path)
273 self.logger.info("\tSystem Type: %s" % machine_type)
George Keishing6ea92b02021-07-01 11:20:50 -0500274 for k, v in ffdc_actions[machine_type].items():
Peter D Phan72ce6b82021-06-03 06:18:26 -0500275
George Keishing6ea92b02021-07-01 11:20:50 -0500276 if self.remote_protocol != ffdc_actions[machine_type][k]['PROTOCOL'][0] \
277 and self.remote_protocol != 'ALL':
278 continue
Peter D Phan72ce6b82021-06-03 06:18:26 -0500279
Peter D Phanbabf2962021-07-07 11:24:40 -0500280 if ffdc_actions[machine_type][k]['PROTOCOL'][0] == 'SSH' \
281 or ffdc_actions[machine_type][k]['PROTOCOL'][0] == 'SCP':
282 if 'SSH' in working_protocol_list \
283 or 'SCP' in working_protocol_list:
Peter D Phan3beb02e2021-07-06 13:25:17 -0500284 self.protocol_ssh(ffdc_actions, machine_type, k)
285 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500286 self.logger.error("\n\tERROR: SSH or SCP is not available for %s." % self.hostname)
George Keishing6ea92b02021-07-01 11:20:50 -0500287
Peter D Phan5963d632021-07-12 09:58:55 -0500288 if ffdc_actions[machine_type][k]['PROTOCOL'][0] == 'TELNET':
289 if 'TELNET' in working_protocol_list:
290 self.protocol_telnet(ffdc_actions, machine_type, k)
291 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500292 self.logger.error("\n\tERROR: TELNET is not available for %s." % self.hostname)
Peter D Phan5963d632021-07-12 09:58:55 -0500293
George Keishing6ea92b02021-07-01 11:20:50 -0500294 if ffdc_actions[machine_type][k]['PROTOCOL'][0] == 'REDFISH':
Peter D Phan3beb02e2021-07-06 13:25:17 -0500295 if 'REDFISH' in working_protocol_list:
296 self.protocol_redfish(ffdc_actions, machine_type, k)
297 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500298 self.logger.error("\n\tERROR: REDFISH is not available for %s." % self.hostname)
George Keishing6ea92b02021-07-01 11:20:50 -0500299
300 if ffdc_actions[machine_type][k]['PROTOCOL'][0] == 'IPMI':
Peter D Phan3beb02e2021-07-06 13:25:17 -0500301 if 'IPMI' in working_protocol_list:
302 self.protocol_ipmi(ffdc_actions, machine_type, k)
303 else:
Peter D Phand1fccd32021-07-21 06:45:54 -0500304 self.logger.error("\n\tERROR: IPMI is not available for %s." % self.hostname)
George Keishing04d29102021-07-16 02:05:57 -0500305
306 if ffdc_actions[machine_type][k]['PROTOCOL'][0] == 'SHELL':
307 if 'SHELL' in working_protocol_list:
308 self.protocol_shell_script(ffdc_actions, machine_type, k)
309 else:
310 self.logger.error("\n\tERROR: can't execute SHELL script")
George Keishingeafba182021-06-29 13:44:58 -0500311
Peter D Phan04aca3b2021-06-21 10:37:18 -0500312 # Close network connection after collecting all files
Peter D Phan7610bc42021-07-06 06:31:05 -0500313 self.elapsed_time = time.strftime("%H:%M:%S", time.gmtime(time.time() - self.start_time))
Peter D Phan5963d632021-07-12 09:58:55 -0500314 self.ssh_remoteclient.ssh_remoteclient_disconnect()
315 self.telnet_remoteclient.tn_remoteclient_disconnect()
Peter D Phan04aca3b2021-06-21 10:37:18 -0500316
Peter D Phan0c669772021-06-24 13:52:42 -0500317 def protocol_ssh(self,
318 ffdc_actions,
George Keishing6ea92b02021-07-01 11:20:50 -0500319 machine_type,
320 sub_type):
Peter D Phan0c669772021-06-24 13:52:42 -0500321 r"""
322 Perform actions using SSH and SCP protocols.
323
324 Description of argument(s):
325 ffdc_actions List of actions from ffdc_config.yaml.
326 machine_type OS Type of remote host.
George Keishing6ea92b02021-07-01 11:20:50 -0500327 sub_type Group type of commands.
Peter D Phan0c669772021-06-24 13:52:42 -0500328 """
329
George Keishing6ea92b02021-07-01 11:20:50 -0500330 if sub_type == 'DUMP_LOGS':
331 self.group_copy(ffdc_actions[machine_type][sub_type])
332 else:
333 self.collect_and_copy_ffdc(ffdc_actions[machine_type][sub_type])
Peter D Phan0c669772021-06-24 13:52:42 -0500334
Peter D Phan5963d632021-07-12 09:58:55 -0500335 def protocol_telnet(self,
336 ffdc_actions,
337 machine_type,
338 sub_type):
339 r"""
340 Perform actions using telnet protocol.
341 Description of argument(s):
342 ffdc_actions List of actions from ffdc_config.yaml.
343 machine_type OS Type of remote host.
344 """
Peter D Phane86d9a52021-07-15 10:42:25 -0500345 self.logger.info("\n\t[Run] Executing commands on %s using %s" % (self.hostname, 'TELNET'))
Peter D Phan5963d632021-07-12 09:58:55 -0500346 telnet_files_saved = []
347 progress_counter = 0
348 list_of_commands = ffdc_actions[machine_type][sub_type]['COMMANDS']
349 for index, each_cmd in enumerate(list_of_commands, start=0):
350 command_txt, command_timeout = self.unpack_command(each_cmd)
351 result = self.telnet_remoteclient.execute_command(command_txt, command_timeout)
352 if result:
353 try:
354 targ_file = ffdc_actions[machine_type][sub_type]['FILES'][index]
355 except IndexError:
Peter D Phane86d9a52021-07-15 10:42:25 -0500356 targ_file = command_txt
357 self.logger.warning(
358 "\n\t[WARN] Missing filename to store data from telnet %s." % each_cmd)
359 self.logger.warning("\t[WARN] Data will be stored in %s." % targ_file)
Peter D Phan5963d632021-07-12 09:58:55 -0500360 targ_file_with_path = (self.ffdc_dir_path
361 + self.ffdc_prefix
362 + targ_file)
363 # Creates a new file
364 with open(targ_file_with_path, 'wb') as fp:
365 fp.write(result)
366 fp.close
367 telnet_files_saved.append(targ_file)
368 progress_counter += 1
369 self.print_progress(progress_counter)
Peter D Phane86d9a52021-07-15 10:42:25 -0500370 self.logger.info("\n\t[Run] Commands execution completed.\t\t [OK]")
Peter D Phan5963d632021-07-12 09:58:55 -0500371 for file in telnet_files_saved:
Peter D Phane86d9a52021-07-15 10:42:25 -0500372 self.logger.info("\n\t\tSuccessfully save file " + file + ".")
Peter D Phan5963d632021-07-12 09:58:55 -0500373
Peter D Phan0c669772021-06-24 13:52:42 -0500374 def protocol_redfish(self,
375 ffdc_actions,
George Keishing6ea92b02021-07-01 11:20:50 -0500376 machine_type,
377 sub_type):
Peter D Phan0c669772021-06-24 13:52:42 -0500378 r"""
379 Perform actions using Redfish protocol.
380
381 Description of argument(s):
382 ffdc_actions List of actions from ffdc_config.yaml.
383 machine_type OS Type of remote host.
George Keishing6ea92b02021-07-01 11:20:50 -0500384 sub_type Group type of commands.
Peter D Phan0c669772021-06-24 13:52:42 -0500385 """
386
Peter D Phane86d9a52021-07-15 10:42:25 -0500387 self.logger.info("\n\t[Run] Executing commands to %s using %s" % (self.hostname, 'REDFISH'))
Peter D Phan0c669772021-06-24 13:52:42 -0500388 redfish_files_saved = []
389 progress_counter = 0
George Keishing6ea92b02021-07-01 11:20:50 -0500390 list_of_URL = ffdc_actions[machine_type][sub_type]['URL']
Peter D Phan0c669772021-06-24 13:52:42 -0500391 for index, each_url in enumerate(list_of_URL, start=0):
392 redfish_parm = '-u ' + self.username + ' -p ' + self.password + ' -r ' \
393 + self.hostname + ' -S Always raw GET ' + each_url
394
395 result = self.run_redfishtool(redfish_parm)
396 if result:
397 try:
Peter D Phanbabf2962021-07-07 11:24:40 -0500398 targ_file = self.get_file_list(ffdc_actions[machine_type][sub_type])[index]
Peter D Phan0c669772021-06-24 13:52:42 -0500399 except IndexError:
400 targ_file = each_url.split('/')[-1]
Peter D Phane86d9a52021-07-15 10:42:25 -0500401 self.logger.warning(
402 "\n\t[WARN] Missing filename to store data from redfish URL %s." % each_url)
403 self.logger.warning("\t[WARN] Data will be stored in %s." % targ_file)
Peter D Phan0c669772021-06-24 13:52:42 -0500404
405 targ_file_with_path = (self.ffdc_dir_path
406 + self.ffdc_prefix
407 + targ_file)
408
409 # Creates a new file
410 with open(targ_file_with_path, 'w') as fp:
411 fp.write(result)
412 fp.close
413 redfish_files_saved.append(targ_file)
414
415 progress_counter += 1
416 self.print_progress(progress_counter)
417
Peter D Phane86d9a52021-07-15 10:42:25 -0500418 self.logger.info("\n\t[Run] Commands execution completed.\t\t [OK]")
Peter D Phan0c669772021-06-24 13:52:42 -0500419
420 for file in redfish_files_saved:
Peter D Phane86d9a52021-07-15 10:42:25 -0500421 self.logger.info("\n\t\tSuccessfully save file " + file + ".")
Peter D Phan0c669772021-06-24 13:52:42 -0500422
George Keishingeafba182021-06-29 13:44:58 -0500423 def protocol_ipmi(self,
424 ffdc_actions,
George Keishing6ea92b02021-07-01 11:20:50 -0500425 machine_type,
426 sub_type):
George Keishingeafba182021-06-29 13:44:58 -0500427 r"""
428 Perform actions using ipmitool over LAN protocol.
429
430 Description of argument(s):
431 ffdc_actions List of actions from ffdc_config.yaml.
432 machine_type OS Type of remote host.
George Keishing6ea92b02021-07-01 11:20:50 -0500433 sub_type Group type of commands.
George Keishingeafba182021-06-29 13:44:58 -0500434 """
435
Peter D Phane86d9a52021-07-15 10:42:25 -0500436 self.logger.info("\n\t[Run] Executing commands to %s using %s" % (self.hostname, 'IPMI'))
George Keishingeafba182021-06-29 13:44:58 -0500437 ipmi_files_saved = []
438 progress_counter = 0
Peter D Phanbabf2962021-07-07 11:24:40 -0500439 list_of_cmd = self.get_command_list(ffdc_actions[machine_type][sub_type])
George Keishingeafba182021-06-29 13:44:58 -0500440 for index, each_cmd in enumerate(list_of_cmd, start=0):
441 ipmi_parm = '-U ' + self.username + ' -P ' + self.password + ' -H ' \
Peter D Phand1fccd32021-07-21 06:45:54 -0500442 + self.hostname + ' -I lanplus ' + each_cmd
George Keishingeafba182021-06-29 13:44:58 -0500443
444 result = self.run_ipmitool(ipmi_parm)
445 if result:
446 try:
Peter D Phanbabf2962021-07-07 11:24:40 -0500447 targ_file = self.get_file_list(ffdc_actions[machine_type][sub_type])[index]
George Keishingeafba182021-06-29 13:44:58 -0500448 except IndexError:
George Keishing6ea92b02021-07-01 11:20:50 -0500449 targ_file = each_cmd.split('/')[-1]
Peter D Phane86d9a52021-07-15 10:42:25 -0500450 self.logger.warning("\n\t[WARN] Missing filename to store data from IPMI %s." % each_cmd)
451 self.logger.warning("\t[WARN] Data will be stored in %s." % targ_file)
George Keishingeafba182021-06-29 13:44:58 -0500452
453 targ_file_with_path = (self.ffdc_dir_path
454 + self.ffdc_prefix
455 + targ_file)
456
457 # Creates a new file
458 with open(targ_file_with_path, 'w') as fp:
459 fp.write(result)
460 fp.close
461 ipmi_files_saved.append(targ_file)
462
463 progress_counter += 1
464 self.print_progress(progress_counter)
465
Peter D Phane86d9a52021-07-15 10:42:25 -0500466 self.logger.info("\n\t[Run] Commands execution completed.\t\t [OK]")
George Keishingeafba182021-06-29 13:44:58 -0500467
468 for file in ipmi_files_saved:
Peter D Phane86d9a52021-07-15 10:42:25 -0500469 self.logger.info("\n\t\tSuccessfully save file " + file + ".")
George Keishingeafba182021-06-29 13:44:58 -0500470
Peter D Phan04aca3b2021-06-21 10:37:18 -0500471 def collect_and_copy_ffdc(self,
Peter D Phan2b8052d2021-06-22 10:55:41 -0500472 ffdc_actions_for_machine_type,
473 form_filename=False):
Peter D Phan04aca3b2021-06-21 10:37:18 -0500474 r"""
475 Send commands in ffdc_config file to targeted system.
476
477 Description of argument(s):
478 ffdc_actions_for_machine_type commands and files for the selected remote host type.
Peter D Phan2b8052d2021-06-22 10:55:41 -0500479 form_filename if true, pre-pend self.target_type to filename
Peter D Phan04aca3b2021-06-21 10:37:18 -0500480 """
481
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500482 # Executing commands, if any
Peter D Phan3beb02e2021-07-06 13:25:17 -0500483 self.ssh_execute_ffdc_commands(ffdc_actions_for_machine_type,
484 form_filename)
Peter D Phan04aca3b2021-06-21 10:37:18 -0500485
Peter D Phan3beb02e2021-07-06 13:25:17 -0500486 # Copying files
Peter D Phan5963d632021-07-12 09:58:55 -0500487 if self.ssh_remoteclient.scpclient:
Peter D Phane86d9a52021-07-15 10:42:25 -0500488 self.logger.info("\n\n\tCopying FFDC files from remote system %s.\n" % self.hostname)
Peter D Phan2b8052d2021-06-22 10:55:41 -0500489
Peter D Phan04aca3b2021-06-21 10:37:18 -0500490 # Retrieving files from target system
Peter D Phanbabf2962021-07-07 11:24:40 -0500491 list_of_files = self.get_file_list(ffdc_actions_for_machine_type)
Peter D Phan2b8052d2021-06-22 10:55:41 -0500492 self.scp_ffdc(self.ffdc_dir_path, self.ffdc_prefix, form_filename, list_of_files)
Peter D Phan04aca3b2021-06-21 10:37:18 -0500493 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500494 self.logger.info("\n\n\tSkip copying FFDC files from remote system %s.\n" % self.hostname)
Peter D Phan04aca3b2021-06-21 10:37:18 -0500495
Peter D Phanbabf2962021-07-07 11:24:40 -0500496 def get_command_list(self,
497 ffdc_actions_for_machine_type):
498 r"""
499 Fetch list of commands from configuration file
500
501 Description of argument(s):
502 ffdc_actions_for_machine_type commands and files for the selected remote host type.
503 """
504 try:
505 list_of_commands = ffdc_actions_for_machine_type['COMMANDS']
506 except KeyError:
507 list_of_commands = []
508 return list_of_commands
509
510 def get_file_list(self,
511 ffdc_actions_for_machine_type):
512 r"""
513 Fetch list of commands from configuration file
514
515 Description of argument(s):
516 ffdc_actions_for_machine_type commands and files for the selected remote host type.
517 """
518 try:
519 list_of_files = ffdc_actions_for_machine_type['FILES']
520 except KeyError:
521 list_of_files = []
522 return list_of_files
523
Peter D Phan5963d632021-07-12 09:58:55 -0500524 def unpack_command(self,
525 command):
526 r"""
527 Unpack command from config file
528
529 Description of argument(s):
530 command Command from config file.
531 """
532 if isinstance(command, dict):
533 command_txt = next(iter(command))
534 command_timeout = next(iter(command.values()))
535 elif isinstance(command, str):
536 command_txt = command
537 # Default command timeout 60 seconds
538 command_timeout = 60
539
540 return command_txt, command_timeout
541
Peter D Phan3beb02e2021-07-06 13:25:17 -0500542 def ssh_execute_ffdc_commands(self,
543 ffdc_actions_for_machine_type,
544 form_filename=False):
545 r"""
546 Send commands in ffdc_config file to targeted system.
547
548 Description of argument(s):
549 ffdc_actions_for_machine_type commands and files for the selected remote host type.
550 form_filename if true, pre-pend self.target_type to filename
551 """
Peter D Phane86d9a52021-07-15 10:42:25 -0500552 self.logger.info("\n\t[Run] Executing commands on %s using %s"
553 % (self.hostname, ffdc_actions_for_machine_type['PROTOCOL'][0]))
Peter D Phan3beb02e2021-07-06 13:25:17 -0500554
Peter D Phanbabf2962021-07-07 11:24:40 -0500555 list_of_commands = self.get_command_list(ffdc_actions_for_machine_type)
Peter D Phan3beb02e2021-07-06 13:25:17 -0500556 # If command list is empty, returns
557 if not list_of_commands:
558 return
559
560 progress_counter = 0
561 for command in list_of_commands:
Peter D Phan5963d632021-07-12 09:58:55 -0500562 command_txt, command_timeout = self.unpack_command(command)
Peter D Phan3beb02e2021-07-06 13:25:17 -0500563
564 if form_filename:
565 command_txt = str(command_txt % self.target_type)
566
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500567 cmd_exit_code, err, response = \
568 self.ssh_remoteclient.execute_command(command_txt, command_timeout)
569
570 if cmd_exit_code:
571 self.logger.warning(
572 "\n\t\t[WARN] %s exits with code %s." % (command_txt, str(cmd_exit_code)))
573 self.logger.warning("\t\t[WARN] %s " % err)
Peter D Phanbabf2962021-07-07 11:24:40 -0500574
Peter D Phan3beb02e2021-07-06 13:25:17 -0500575 progress_counter += 1
576 self.print_progress(progress_counter)
577
Peter D Phane86d9a52021-07-15 10:42:25 -0500578 self.logger.info("\n\t[Run] Commands execution completed.\t\t [OK]")
Peter D Phan3beb02e2021-07-06 13:25:17 -0500579
Peter D Phan56429a62021-06-23 08:38:29 -0500580 def group_copy(self,
581 ffdc_actions_for_machine_type):
Peter D Phan56429a62021-06-23 08:38:29 -0500582 r"""
583 scp group of files (wild card) from remote host.
584
585 Description of argument(s):
586 ffdc_actions_for_machine_type commands and files for the selected remote host type.
587 """
Peter D Phan3beb02e2021-07-06 13:25:17 -0500588
Peter D Phan5963d632021-07-12 09:58:55 -0500589 if self.ssh_remoteclient.scpclient:
Peter D Phane86d9a52021-07-15 10:42:25 -0500590 self.logger.info("\n\tCopying DUMP files from remote system %s.\n" % self.hostname)
Peter D Phan56429a62021-06-23 08:38:29 -0500591
Peter D Phanbabf2962021-07-07 11:24:40 -0500592 list_of_commands = self.get_command_list(ffdc_actions_for_machine_type)
593 # If command list is empty, returns
594 if not list_of_commands:
595 return
Peter D Phan56429a62021-06-23 08:38:29 -0500596
Peter D Phanbabf2962021-07-07 11:24:40 -0500597 for command in list_of_commands:
598 try:
599 filename = command.split(' ')[2]
600 except IndexError:
Peter D Phane86d9a52021-07-15 10:42:25 -0500601 self.logger.info("\t\tInvalid command %s for DUMP_LOGS block." % command)
Peter D Phanbabf2962021-07-07 11:24:40 -0500602 continue
603
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500604 cmd_exit_code, err, response = \
605 self.ssh_remoteclient.execute_command(command)
Peter D Phanbabf2962021-07-07 11:24:40 -0500606
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500607 # If file does not exist, code take no action.
608 # cmd_exit_code is ignored for this scenario.
Peter D Phan56429a62021-06-23 08:38:29 -0500609 if response:
Peter D Phan5963d632021-07-12 09:58:55 -0500610 scp_result = self.ssh_remoteclient.scp_file_from_remote(filename, self.ffdc_dir_path)
Peter D Phan56429a62021-06-23 08:38:29 -0500611 if scp_result:
Peter D Phane86d9a52021-07-15 10:42:25 -0500612 self.logger.info("\t\tSuccessfully copied from " + self.hostname + ':' + filename)
Peter D Phan56429a62021-06-23 08:38:29 -0500613 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500614 self.logger.info("\t\tThere is no " + filename)
Peter D Phan56429a62021-06-23 08:38:29 -0500615
616 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500617 self.logger.info("\n\n\tSkip copying files from remote system %s.\n" % self.hostname)
Peter D Phan56429a62021-06-23 08:38:29 -0500618
Peter D Phan72ce6b82021-06-03 06:18:26 -0500619 def scp_ffdc(self,
620 targ_dir_path,
Peter D Phan2b8052d2021-06-22 10:55:41 -0500621 targ_file_prefix,
622 form_filename,
Peter D Phan72ce6b82021-06-03 06:18:26 -0500623 file_list=None,
624 quiet=None):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500625 r"""
626 SCP all files in file_dict to the indicated directory on the local system.
627
628 Description of argument(s):
629 targ_dir_path The path of the directory to receive the files.
630 targ_file_prefix Prefix which will be pre-pended to each
631 target file's name.
632 file_dict A dictionary of files to scp from targeted system to this system
633
634 """
635
Peter D Phan72ce6b82021-06-03 06:18:26 -0500636 progress_counter = 0
637 for filename in file_list:
Peter D Phan2b8052d2021-06-22 10:55:41 -0500638 if form_filename:
639 filename = str(filename % self.target_type)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500640 source_file_path = filename
641 targ_file_path = targ_dir_path + targ_file_prefix + filename.split('/')[-1]
642
Peter D Phanbabf2962021-07-07 11:24:40 -0500643 # If source file name contains wild card, copy filename as is.
644 if '*' in source_file_path:
Peter D Phan5963d632021-07-12 09:58:55 -0500645 scp_result = self.ssh_remoteclient.scp_file_from_remote(source_file_path, self.ffdc_dir_path)
Peter D Phanbabf2962021-07-07 11:24:40 -0500646 else:
Peter D Phan5963d632021-07-12 09:58:55 -0500647 scp_result = self.ssh_remoteclient.scp_file_from_remote(source_file_path, targ_file_path)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500648
649 if not quiet:
650 if scp_result:
Peter D Phane86d9a52021-07-15 10:42:25 -0500651 self.logger.info(
652 "\t\tSuccessfully copied from " + self.hostname + ':' + source_file_path + ".\n")
Peter D Phan72ce6b82021-06-03 06:18:26 -0500653 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500654 self.logger.info(
655 "\t\tFail to copy from " + self.hostname + ':' + source_file_path + ".\n")
Peter D Phan72ce6b82021-06-03 06:18:26 -0500656 else:
657 progress_counter += 1
658 self.print_progress(progress_counter)
659
Peter D Phan72ce6b82021-06-03 06:18:26 -0500660 def set_ffdc_defaults(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500661 r"""
662 Set a default value for self.ffdc_dir_path and self.ffdc_prefix.
663 Collected ffdc file will be stored in dir /self.location/hostname_timestr/.
664 Individual ffdc file will have timestr_filename.
665
666 Description of class variables:
667 self.ffdc_dir_path The dir path where collected ffdc data files should be put.
668
669 self.ffdc_prefix The prefix to be given to each ffdc file name.
670
671 """
672
673 timestr = time.strftime("%Y%m%d-%H%M%S")
674 self.ffdc_dir_path = self.location + "/" + self.hostname + "_" + timestr + "/"
675 self.ffdc_prefix = timestr + "_"
676 self.validate_local_store(self.ffdc_dir_path)
677
678 def validate_local_store(self, dir_path):
679 r"""
680 Ensure path exists to store FFDC files locally.
681
682 Description of variable:
683 dir_path The dir path where collected ffdc data files will be stored.
684
685 """
686
687 if not os.path.exists(dir_path):
688 try:
George Keishing7b3a5132021-07-13 09:24:02 -0500689 os.makedirs(dir_path, 0o755)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500690 except (IOError, OSError) as e:
691 # PermissionError
692 if e.errno == EPERM or e.errno == EACCES:
Peter D Phane86d9a52021-07-15 10:42:25 -0500693 self.logger.error(
694 '>>>>>\tERROR: os.makedirs %s failed with PermissionError.\n' % dir_path)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500695 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500696 self.logger.error(
697 '>>>>>\tERROR: os.makedirs %s failed with %s.\n' % (dir_path, e.strerror))
Peter D Phan72ce6b82021-06-03 06:18:26 -0500698 sys.exit(-1)
699
700 def print_progress(self, progress):
701 r"""
702 Print activity progress +
703
704 Description of variable:
705 progress Progress counter.
706
707 """
708
709 sys.stdout.write("\r\t" + "+" * progress)
710 sys.stdout.flush()
711 time.sleep(.1)
Peter D Phan0c669772021-06-24 13:52:42 -0500712
713 def verify_redfish(self):
714 r"""
715 Verify remote host has redfish service active
716
717 """
718 redfish_parm = '-u ' + self.username + ' -p ' + self.password + ' -r ' \
719 + self.hostname + ' -S Always raw GET /redfish/v1/'
720 return(self.run_redfishtool(redfish_parm, True))
721
George Keishingeafba182021-06-29 13:44:58 -0500722 def verify_ipmi(self):
723 r"""
724 Verify remote host has IPMI LAN service active
725
726 """
727 ipmi_parm = '-U ' + self.username + ' -P ' + self.password + ' -H ' \
Peter D Phand1fccd32021-07-21 06:45:54 -0500728 + self.hostname + ' power status -I lanplus'
George Keishingeafba182021-06-29 13:44:58 -0500729 return(self.run_ipmitool(ipmi_parm, True))
730
Peter D Phan0c669772021-06-24 13:52:42 -0500731 def run_redfishtool(self,
732 parms_string,
733 quiet=False):
734 r"""
735 Run CLI redfishtool
736
737 Description of variable:
738 parms_string redfishtool subcommand and options.
739 quiet do not print redfishtool error message if True
740 """
741
742 result = subprocess.run(['redfishtool ' + parms_string],
743 stdout=subprocess.PIPE,
744 stderr=subprocess.PIPE,
745 shell=True,
746 universal_newlines=True)
747
748 if result.stderr and not quiet:
Peter D Phane86d9a52021-07-15 10:42:25 -0500749 self.logger.error('\n\t\tERROR with redfishtool ' + parms_string)
750 self.logger.error('\t\t' + result.stderr)
Peter D Phan0c669772021-06-24 13:52:42 -0500751
752 return result.stdout
George Keishingeafba182021-06-29 13:44:58 -0500753
754 def run_ipmitool(self,
755 parms_string,
756 quiet=False):
757 r"""
758 Run CLI IPMI tool.
759
760 Description of variable:
761 parms_string ipmitool subcommand and options.
762 quiet do not print redfishtool error message if True
763 """
764
765 result = subprocess.run(['ipmitool -I lanplus -C 17 ' + parms_string],
766 stdout=subprocess.PIPE,
767 stderr=subprocess.PIPE,
768 shell=True,
769 universal_newlines=True)
770
771 if result.stderr and not quiet:
Peter D Phane86d9a52021-07-15 10:42:25 -0500772 self.logger.error('\n\t\tERROR with ipmitool -I lanplus -C 17 ' + parms_string)
773 self.logger.error('\t\t' + result.stderr)
George Keishingeafba182021-06-29 13:44:58 -0500774
775 return result.stdout
George Keishing04d29102021-07-16 02:05:57 -0500776
777 def run_shell_script(self,
778 parms_string,
779 quiet=False):
780 r"""
781 Run CLI shell script tool.
782
783 Description of variable:
784 parms_string script command options.
785 quiet do not print redfishtool error message if True
786 """
787
788 result = subprocess.run([parms_string],
789 stdout=subprocess.PIPE,
790 stderr=subprocess.PIPE,
791 shell=True,
792 universal_newlines=True)
793
794 if result.stderr and not quiet:
795 self.logger.error('\n\t\tERROR executing %s' % parms_string)
796 self.logger.error('\t\t' + result.stderr)
797
798 return result.stdout
799
800 def protocol_shell_script(self,
801 ffdc_actions,
802 machine_type,
803 sub_type):
804 r"""
805 Perform SHELL script execution locally.
806
807 Description of argument(s):
808 ffdc_actions List of actions from ffdc_config.yaml.
809 machine_type OS Type of remote host.
810 sub_type Group type of commands.
811 """
812
813 self.logger.info("\n\t[Run] Executing commands to %s using %s" % (self.hostname, 'SHELL'))
814 shell_files_saved = []
815 progress_counter = 0
816 list_of_cmd = self.get_command_list(ffdc_actions[machine_type][sub_type])
817 for index, each_cmd in enumerate(list_of_cmd, start=0):
818
819 result = self.run_shell_script(each_cmd)
820 if result:
821 try:
822 targ_file = self.get_file_list(ffdc_actions[machine_type][sub_type])[index]
823 except IndexError:
824 targ_file = each_cmd.split('/')[-1]
825 self.logger.warning("\n\t[WARN] Missing filename to store data %s." % each_cmd)
826 self.logger.warning("\t[WARN] Data will be stored in %s." % targ_file)
827
828 targ_file_with_path = (self.ffdc_dir_path
829 + self.ffdc_prefix
830 + targ_file)
831
832 # Creates a new file
833 with open(targ_file_with_path, 'w') as fp:
834 fp.write(result)
835 fp.close
836 shell_files_saved.append(targ_file)
837
838 progress_counter += 1
839 self.print_progress(progress_counter)
840
841 self.logger.info("\n\t[Run] Commands execution completed.\t\t [OK]")
842
843 for file in shell_files_saved:
844 self.logger.info("\n\t\tSuccessfully save file " + file + ".")