blob: f016e851e1e238c8ed463d5508573d333b3278ec [file] [log] [blame]
George Keishinge7e91712021-09-03 11:28:44 -05001#!/usr/bin/env python3
Peter D Phan72ce6b82021-06-03 06:18:26 -05002
3r"""
4See class prolog below for details.
5"""
6
Patrick Williams20f38712022-12-08 06:18:26 -06007import json
8import logging
9import os
10import platform
11import re
12import subprocess
13import sys
14import time
George Keishing09679892022-12-08 08:21:52 -060015from errno import EACCES, EPERM
16
George Keishinge635ddc2022-12-08 07:38:02 -060017import yaml
Peter D Phan5e56f522021-12-20 13:19:41 -060018
Peter D Phancb791d72022-02-08 12:23:03 -060019script_dir = os.path.dirname(os.path.abspath(__file__))
20sys.path.append(script_dir)
21# Walk path and append to sys.path
22for root, dirs, files in os.walk(script_dir):
23 for dir in dirs:
24 sys.path.append(os.path.join(root, dir))
25
Patrick Williams20f38712022-12-08 06:18:26 -060026from ssh_utility import SSHRemoteclient # NOQA
27from telnet_utility import TelnetRemoteclient # NOQA
Peter D Phan72ce6b82021-06-03 06:18:26 -050028
George Keishingb97a9042021-07-29 07:41:20 -050029r"""
30User define plugins python functions.
31
32It will imports files from directory plugins
33
34plugins
35├── file1.py
36└── file2.py
37
38Example how to define in YAML:
39 - plugin:
40 - plugin_name: plugin.foo_func.foo_func_yaml
41 - plugin_args:
42 - arg1
43 - arg2
44"""
Patrick Williams20f38712022-12-08 06:18:26 -060045plugin_dir = __file__.split(__file__.split("/")[-1])[0] + "/plugins"
Peter D Phan5e56f522021-12-20 13:19:41 -060046sys.path.append(plugin_dir)
George Keishingb97a9042021-07-29 07:41:20 -050047try:
48 for module in os.listdir(plugin_dir):
Patrick Williams20f38712022-12-08 06:18:26 -060049 if module == "__init__.py" or module[-3:] != ".py":
George Keishingb97a9042021-07-29 07:41:20 -050050 continue
51 plugin_module = "plugins." + module[:-3]
52 # To access the module plugin.<module name>.<function>
53 # Example: plugin.foo_func.foo_func_yaml()
54 try:
55 plugin = __import__(plugin_module, globals(), locals(), [], 0)
56 except Exception as e:
57 print("PLUGIN: Module import failed: %s" % module)
58 pass
59except FileNotFoundError as e:
60 print("PLUGIN: %s" % e)
61 pass
62
63r"""
64This is for plugin functions returning data or responses to the caller
65in YAML plugin setup.
66
67Example:
68
69 - plugin:
70 - plugin_name: version = plugin.ssh_execution.ssh_execute_cmd
71 - plugin_args:
72 - ${hostname}
73 - ${username}
74 - ${password}
75 - "cat /etc/os-release | grep VERSION_ID | awk -F'=' '{print $2}'"
76 - plugin:
77 - plugin_name: plugin.print_vars.print_vars
78 - plugin_args:
79 - version
80
81where first plugin "version" var is used by another plugin in the YAML
82block or plugin
83
84"""
85global global_log_store_path
86global global_plugin_dict
87global global_plugin_list
George Keishing9348b402021-08-13 12:22:35 -050088
George Keishing0581cb02021-08-05 15:08:58 -050089# Hold the plugin return values in dict and plugin return vars in list.
George Keishing9348b402021-08-13 12:22:35 -050090# Dict is to reference and update vars processing in parser where as
91# list is for current vars from the plugin block which needs processing.
George Keishingb97a9042021-07-29 07:41:20 -050092global_plugin_dict = {}
93global_plugin_list = []
George Keishing9348b402021-08-13 12:22:35 -050094
George Keishing0581cb02021-08-05 15:08:58 -050095# Hold the plugin return named declared if function returned values are list,dict.
96# Refer this name list to look up the plugin dict for eval() args function
George Keishing9348b402021-08-13 12:22:35 -050097# Example ['version']
George Keishing0581cb02021-08-05 15:08:58 -050098global_plugin_type_list = []
George Keishing9348b402021-08-13 12:22:35 -050099
100# Path where logs are to be stored or written.
Patrick Williams20f38712022-12-08 06:18:26 -0600101global_log_store_path = ""
George Keishingb97a9042021-07-29 07:41:20 -0500102
George Keishing1e7b0182021-08-06 14:05:54 -0500103# Plugin error state defaults.
104plugin_error_dict = {
Patrick Williams20f38712022-12-08 06:18:26 -0600105 "exit_on_error": False,
106 "continue_on_error": False,
George Keishing1e7b0182021-08-06 14:05:54 -0500107}
108
Peter D Phan72ce6b82021-06-03 06:18:26 -0500109
Peter D Phan5e56f522021-12-20 13:19:41 -0600110class ffdc_collector:
Peter D Phan72ce6b82021-06-03 06:18:26 -0500111 r"""
George Keishing1e7b0182021-08-06 14:05:54 -0500112 Execute commands from configuration file to collect log files.
Peter D Phan72ce6b82021-06-03 06:18:26 -0500113 Fetch and store generated files at the specified location.
114
115 """
116
Patrick Williams20f38712022-12-08 06:18:26 -0600117 def __init__(
118 self,
119 hostname,
120 username,
121 password,
George Keishing7a61aa22023-06-26 13:18:37 +0530122 port_ssh,
George Keishinge8a41752023-06-22 21:42:47 +0530123 port_https,
124 port_ipmi,
Patrick Williams20f38712022-12-08 06:18:26 -0600125 ffdc_config,
126 location,
127 remote_type,
128 remote_protocol,
129 env_vars,
130 econfig,
131 log_level,
132 ):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500133 r"""
134 Description of argument(s):
135
George Keishing8e94f8c2021-07-23 15:06:32 -0500136 hostname name/ip of the targeted (remote) system
137 username user on the targeted system with access to FFDC files
138 password password for user on targeted system
George Keishing7a61aa22023-06-26 13:18:37 +0530139 port_ssh SSH port value. By default 22
George Keishinge8a41752023-06-22 21:42:47 +0530140 port_https HTTPS port value. By default 443
141 port_ipmi IPMI port value. By default 623
George Keishing8e94f8c2021-07-23 15:06:32 -0500142 ffdc_config configuration file listing commands and files for FFDC
143 location where to store collected FFDC
144 remote_type os type of the remote host
145 remote_protocol Protocol to use to collect data
146 env_vars User define CLI env vars '{"key : "value"}'
147 econfig User define env vars YAML file
Peter D Phan72ce6b82021-06-03 06:18:26 -0500148
149 """
Peter D Phane86d9a52021-07-15 10:42:25 -0500150
151 self.hostname = hostname
152 self.username = username
153 self.password = password
George Keishing7a61aa22023-06-26 13:18:37 +0530154 self.port_ssh = str(port_ssh)
George Keishinge8a41752023-06-22 21:42:47 +0530155 self.port_https = str(port_https)
156 self.port_ipmi = str(port_ipmi)
Peter D Phane86d9a52021-07-15 10:42:25 -0500157 self.ffdc_config = ffdc_config
158 self.location = location + "/" + remote_type.upper()
159 self.ssh_remoteclient = None
160 self.telnet_remoteclient = None
161 self.ffdc_dir_path = ""
162 self.ffdc_prefix = ""
163 self.target_type = remote_type.upper()
164 self.remote_protocol = remote_protocol.upper()
George Keishinge1686752021-07-27 12:55:28 -0500165 self.env_vars = env_vars
166 self.econfig = econfig
Peter D Phane86d9a52021-07-15 10:42:25 -0500167 self.start_time = 0
Patrick Williams20f38712022-12-08 06:18:26 -0600168 self.elapsed_time = ""
Peter D Phane86d9a52021-07-15 10:42:25 -0500169 self.logger = None
170
171 # Set prefix values for scp files and directory.
172 # Since the time stamp is at second granularity, these values are set here
173 # to be sure that all files for this run will have same timestamps
174 # and they will be saved in the same directory.
175 # self.location == local system for now
Peter D Phan5e56f522021-12-20 13:19:41 -0600176 self.set_ffdc_default_store_path()
Peter D Phane86d9a52021-07-15 10:42:25 -0500177
Peter D Phan5e56f522021-12-20 13:19:41 -0600178 # Logger for this run. Need to be after set_ffdc_default_store_path()
Peter D Phane86d9a52021-07-15 10:42:25 -0500179 self.script_logging(getattr(logging, log_level.upper()))
180
181 # Verify top level directory exists for storage
182 self.validate_local_store(self.location)
183
Peter D Phan72ce6b82021-06-03 06:18:26 -0500184 if self.verify_script_env():
Peter D Phane86d9a52021-07-15 10:42:25 -0500185 # Load default or user define YAML configuration file.
Patrick Williams20f38712022-12-08 06:18:26 -0600186 with open(self.ffdc_config, "r") as file:
George Keishinge9b23d32021-08-13 12:57:58 -0500187 try:
Yunyun Linf87cc0a2022-06-08 16:57:04 -0700188 self.ffdc_actions = yaml.load(file, Loader=yaml.SafeLoader)
George Keishinge9b23d32021-08-13 12:57:58 -0500189 except yaml.YAMLError as e:
190 self.logger.error(e)
191 sys.exit(-1)
Peter D Phane86d9a52021-07-15 10:42:25 -0500192
193 if self.target_type not in self.ffdc_actions.keys():
194 self.logger.error(
Patrick Williams20f38712022-12-08 06:18:26 -0600195 "\n\tERROR: %s is not listed in %s.\n\n"
196 % (self.target_type, self.ffdc_config)
197 )
Peter D Phane86d9a52021-07-15 10:42:25 -0500198 sys.exit(-1)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500199 else:
Peter D Phan8462faf2021-06-16 12:24:15 -0500200 sys.exit(-1)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500201
George Keishing4885b2f2021-07-21 15:22:45 -0500202 # Load ENV vars from user.
George Keishingaa1f8482021-07-22 00:54:55 -0500203 self.logger.info("\n\tENV: User define input YAML variables")
204 self.env_dict = {}
Peter D Phan5e56f522021-12-20 13:19:41 -0600205 self.load_env()
George Keishingaa1f8482021-07-22 00:54:55 -0500206
Peter D Phan72ce6b82021-06-03 06:18:26 -0500207 def verify_script_env(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500208 # Import to log version
209 import click
210 import paramiko
211
212 run_env_ok = True
Peter D Phan0c669772021-06-24 13:52:42 -0500213
Patrick Williams20f38712022-12-08 06:18:26 -0600214 redfishtool_version = (
215 self.run_tool_cmd("redfishtool -V").split(" ")[2].strip("\n")
216 )
217 ipmitool_version = self.run_tool_cmd("ipmitool -V").split(" ")[2]
Peter D Phan0c669772021-06-24 13:52:42 -0500218
Peter D Phane86d9a52021-07-15 10:42:25 -0500219 self.logger.info("\n\t---- Script host environment ----")
Patrick Williams20f38712022-12-08 06:18:26 -0600220 self.logger.info(
221 "\t{:<10} {:<10}".format("Script hostname", os.uname()[1])
222 )
223 self.logger.info(
224 "\t{:<10} {:<10}".format("Script host os", platform.platform())
225 )
226 self.logger.info(
227 "\t{:<10} {:>10}".format("Python", platform.python_version())
228 )
229 self.logger.info("\t{:<10} {:>10}".format("PyYAML", yaml.__version__))
230 self.logger.info("\t{:<10} {:>10}".format("click", click.__version__))
231 self.logger.info(
232 "\t{:<10} {:>10}".format("paramiko", paramiko.__version__)
233 )
234 self.logger.info(
235 "\t{:<10} {:>9}".format("redfishtool", redfishtool_version)
236 )
237 self.logger.info(
238 "\t{:<10} {:>12}".format("ipmitool", ipmitool_version)
239 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500240
Patrick Williams20f38712022-12-08 06:18:26 -0600241 if eval(yaml.__version__.replace(".", ",")) < (5, 3, 0):
242 self.logger.error(
243 "\n\tERROR: Python or python packages do not meet minimum"
244 " version requirement."
245 )
246 self.logger.error(
247 "\tERROR: PyYAML version 5.3.0 or higher is needed.\n"
248 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500249 run_env_ok = False
250
Peter D Phane86d9a52021-07-15 10:42:25 -0500251 self.logger.info("\t---- End script host environment ----")
Peter D Phan72ce6b82021-06-03 06:18:26 -0500252 return run_env_ok
253
Patrick Williams20f38712022-12-08 06:18:26 -0600254 def script_logging(self, log_level_attr):
Peter D Phane86d9a52021-07-15 10:42:25 -0500255 r"""
256 Create logger
257
258 """
259 self.logger = logging.getLogger()
260 self.logger.setLevel(log_level_attr)
Patrick Williams20f38712022-12-08 06:18:26 -0600261 log_file_handler = logging.FileHandler(
262 self.ffdc_dir_path + "collector.log"
263 )
Peter D Phane86d9a52021-07-15 10:42:25 -0500264
265 stdout_handler = logging.StreamHandler(sys.stdout)
266 self.logger.addHandler(log_file_handler)
267 self.logger.addHandler(stdout_handler)
268
269 # Turn off paramiko INFO logging
270 logging.getLogger("paramiko").setLevel(logging.WARNING)
271
Peter D Phan72ce6b82021-06-03 06:18:26 -0500272 def target_is_pingable(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500273 r"""
274 Check if target system is ping-able.
275
276 """
George Keishing0662e942021-07-13 05:12:20 -0500277 response = os.system("ping -c 1 %s 2>&1 >/dev/null" % self.hostname)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500278 if response == 0:
Patrick Williams20f38712022-12-08 06:18:26 -0600279 self.logger.info(
280 "\n\t[Check] %s is ping-able.\t\t [OK]" % self.hostname
281 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500282 return True
283 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500284 self.logger.error(
Patrick Williams20f38712022-12-08 06:18:26 -0600285 "\n\tERROR: %s is not ping-able. FFDC collection aborted.\n"
286 % self.hostname
287 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500288 sys.exit(-1)
289
Peter D Phan72ce6b82021-06-03 06:18:26 -0500290 def collect_ffdc(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500291 r"""
292 Initiate FFDC Collection depending on requested protocol.
293
294 """
295
Patrick Williams20f38712022-12-08 06:18:26 -0600296 self.logger.info(
297 "\n\t---- Start communicating with %s ----" % self.hostname
298 )
Peter D Phan7610bc42021-07-06 06:31:05 -0500299 self.start_time = time.time()
Peter D Phan0c669772021-06-24 13:52:42 -0500300
George Keishingf5a57502021-07-22 16:43:47 -0500301 # Find the list of target and protocol supported.
302 check_protocol_list = []
303 config_dict = self.ffdc_actions
Peter D Phan0c669772021-06-24 13:52:42 -0500304
George Keishingf5a57502021-07-22 16:43:47 -0500305 for target_type in config_dict.keys():
306 if self.target_type != target_type:
307 continue
George Keishingeafba182021-06-29 13:44:58 -0500308
George Keishingf5a57502021-07-22 16:43:47 -0500309 for k, v in config_dict[target_type].items():
Patrick Williams20f38712022-12-08 06:18:26 -0600310 if (
311 config_dict[target_type][k]["PROTOCOL"][0]
312 not in check_protocol_list
313 ):
314 check_protocol_list.append(
315 config_dict[target_type][k]["PROTOCOL"][0]
316 )
Peter D Phanbff617a2021-07-22 08:41:35 -0500317
Patrick Williams20f38712022-12-08 06:18:26 -0600318 self.logger.info(
319 "\n\t %s protocol type: %s"
320 % (self.target_type, check_protocol_list)
321 )
Peter D Phanbff617a2021-07-22 08:41:35 -0500322
George Keishingf5a57502021-07-22 16:43:47 -0500323 verified_working_protocol = self.verify_protocol(check_protocol_list)
Peter D Phanbff617a2021-07-22 08:41:35 -0500324
George Keishingf5a57502021-07-22 16:43:47 -0500325 if verified_working_protocol:
Patrick Williams20f38712022-12-08 06:18:26 -0600326 self.logger.info(
327 "\n\t---- Completed protocol pre-requisite check ----\n"
328 )
Peter D Phan0c669772021-06-24 13:52:42 -0500329
George Keishingf5a57502021-07-22 16:43:47 -0500330 # Verify top level directory exists for storage
331 self.validate_local_store(self.location)
332
Patrick Williams20f38712022-12-08 06:18:26 -0600333 if (self.remote_protocol not in verified_working_protocol) and (
334 self.remote_protocol != "ALL"
335 ):
336 self.logger.info(
337 "\n\tWorking protocol list: %s" % verified_working_protocol
338 )
George Keishingf5a57502021-07-22 16:43:47 -0500339 self.logger.error(
Patrick Williams20f38712022-12-08 06:18:26 -0600340 "\tERROR: Requested protocol %s is not in working protocol"
George Keishing7899a452023-02-15 02:46:54 -0600341 " list.\n" % self.remote_protocol
Patrick Williams20f38712022-12-08 06:18:26 -0600342 )
George Keishingf5a57502021-07-22 16:43:47 -0500343 sys.exit(-1)
344 else:
345 self.generate_ffdc(verified_working_protocol)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500346
347 def ssh_to_target_system(self):
348 r"""
349 Open a ssh connection to targeted system.
350
351 """
352
Patrick Williams20f38712022-12-08 06:18:26 -0600353 self.ssh_remoteclient = SSHRemoteclient(
George Keishing7a61aa22023-06-26 13:18:37 +0530354 self.hostname, self.username, self.password, self.port_ssh
Patrick Williams20f38712022-12-08 06:18:26 -0600355 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500356
Peter D Phan5963d632021-07-12 09:58:55 -0500357 if self.ssh_remoteclient.ssh_remoteclient_login():
Patrick Williams20f38712022-12-08 06:18:26 -0600358 self.logger.info(
359 "\n\t[Check] %s SSH connection established.\t [OK]"
360 % self.hostname
361 )
Peter D Phan733df632021-06-17 13:13:36 -0500362
Peter D Phan5963d632021-07-12 09:58:55 -0500363 # Check scp connection.
364 # If scp connection fails,
365 # continue with FFDC generation but skip scp files to local host.
366 self.ssh_remoteclient.scp_connection()
367 return True
368 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600369 self.logger.info(
370 "\n\t[Check] %s SSH connection.\t [NOT AVAILABLE]"
371 % self.hostname
372 )
Peter D Phan5963d632021-07-12 09:58:55 -0500373 return False
374
375 def telnet_to_target_system(self):
376 r"""
377 Open a telnet connection to targeted system.
378 """
Patrick Williams20f38712022-12-08 06:18:26 -0600379 self.telnet_remoteclient = TelnetRemoteclient(
380 self.hostname, self.username, self.password
381 )
Peter D Phan5963d632021-07-12 09:58:55 -0500382 if self.telnet_remoteclient.tn_remoteclient_login():
Patrick Williams20f38712022-12-08 06:18:26 -0600383 self.logger.info(
384 "\n\t[Check] %s Telnet connection established.\t [OK]"
385 % self.hostname
386 )
Peter D Phan5963d632021-07-12 09:58:55 -0500387 return True
388 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600389 self.logger.info(
390 "\n\t[Check] %s Telnet connection.\t [NOT AVAILABLE]"
391 % self.hostname
392 )
Peter D Phan5963d632021-07-12 09:58:55 -0500393 return False
Peter D Phan72ce6b82021-06-03 06:18:26 -0500394
George Keishing772c9772021-06-16 23:23:42 -0500395 def generate_ffdc(self, working_protocol_list):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500396 r"""
Peter D Phan04aca3b2021-06-21 10:37:18 -0500397 Determine actions based on remote host type
Peter D Phan72ce6b82021-06-03 06:18:26 -0500398
Peter D Phan04aca3b2021-06-21 10:37:18 -0500399 Description of argument(s):
400 working_protocol_list list of confirmed working protocols to connect to remote host.
Peter D Phan72ce6b82021-06-03 06:18:26 -0500401 """
402
Patrick Williams20f38712022-12-08 06:18:26 -0600403 self.logger.info(
404 "\n\t---- Executing commands on " + self.hostname + " ----"
405 )
406 self.logger.info(
407 "\n\tWorking protocol list: %s" % working_protocol_list
408 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500409
George Keishingf5a57502021-07-22 16:43:47 -0500410 config_dict = self.ffdc_actions
411 for target_type in config_dict.keys():
412 if self.target_type != target_type:
George Keishing6ea92b02021-07-01 11:20:50 -0500413 continue
Peter D Phan72ce6b82021-06-03 06:18:26 -0500414
Peter D Phane86d9a52021-07-15 10:42:25 -0500415 self.logger.info("\n\tFFDC Path: %s " % self.ffdc_dir_path)
Patrick Williams20f38712022-12-08 06:18:26 -0600416 global_plugin_dict["global_log_store_path"] = self.ffdc_dir_path
George Keishingf5a57502021-07-22 16:43:47 -0500417 self.logger.info("\tSystem Type: %s" % target_type)
418 for k, v in config_dict[target_type].items():
Patrick Williams20f38712022-12-08 06:18:26 -0600419 if (
420 self.remote_protocol not in working_protocol_list
421 and self.remote_protocol != "ALL"
422 ):
George Keishing6ea92b02021-07-01 11:20:50 -0500423 continue
Peter D Phan72ce6b82021-06-03 06:18:26 -0500424
Patrick Williams20f38712022-12-08 06:18:26 -0600425 protocol = config_dict[target_type][k]["PROTOCOL"][0]
George Keishingf5a57502021-07-22 16:43:47 -0500426
427 if protocol not in working_protocol_list:
428 continue
429
George Keishingb7607612021-07-27 13:31:23 -0500430 if protocol in working_protocol_list:
Patrick Williams20f38712022-12-08 06:18:26 -0600431 if protocol == "SSH" or protocol == "SCP":
George Keishing12fd0652021-07-27 13:57:11 -0500432 self.protocol_ssh(protocol, target_type, k)
Patrick Williams20f38712022-12-08 06:18:26 -0600433 elif protocol == "TELNET":
George Keishingf5a57502021-07-22 16:43:47 -0500434 self.protocol_telnet(target_type, k)
Patrick Williams20f38712022-12-08 06:18:26 -0600435 elif (
436 protocol == "REDFISH"
437 or protocol == "IPMI"
438 or protocol == "SHELL"
439 ):
George Keishing506b0582021-07-27 09:31:22 -0500440 self.protocol_execute(protocol, target_type, k)
George Keishingb7607612021-07-27 13:31:23 -0500441 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600442 self.logger.error(
443 "\n\tERROR: %s is not available for %s."
444 % (protocol, self.hostname)
445 )
George Keishingeafba182021-06-29 13:44:58 -0500446
Peter D Phan04aca3b2021-06-21 10:37:18 -0500447 # Close network connection after collecting all files
Patrick Williams20f38712022-12-08 06:18:26 -0600448 self.elapsed_time = time.strftime(
449 "%H:%M:%S", time.gmtime(time.time() - self.start_time)
450 )
Peter D Phanbff617a2021-07-22 08:41:35 -0500451 if self.ssh_remoteclient:
452 self.ssh_remoteclient.ssh_remoteclient_disconnect()
453 if self.telnet_remoteclient:
454 self.telnet_remoteclient.tn_remoteclient_disconnect()
Peter D Phan04aca3b2021-06-21 10:37:18 -0500455
Patrick Williams20f38712022-12-08 06:18:26 -0600456 def protocol_ssh(self, protocol, target_type, sub_type):
Peter D Phan0c669772021-06-24 13:52:42 -0500457 r"""
458 Perform actions using SSH and SCP protocols.
459
460 Description of argument(s):
George Keishing12fd0652021-07-27 13:57:11 -0500461 protocol Protocol to execute.
George Keishingf5a57502021-07-22 16:43:47 -0500462 target_type OS Type of remote host.
George Keishing6ea92b02021-07-01 11:20:50 -0500463 sub_type Group type of commands.
Peter D Phan0c669772021-06-24 13:52:42 -0500464 """
465
Patrick Williams20f38712022-12-08 06:18:26 -0600466 if protocol == "SCP":
George Keishingf5a57502021-07-22 16:43:47 -0500467 self.group_copy(self.ffdc_actions[target_type][sub_type])
George Keishing6ea92b02021-07-01 11:20:50 -0500468 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600469 self.collect_and_copy_ffdc(
470 self.ffdc_actions[target_type][sub_type]
471 )
Peter D Phan0c669772021-06-24 13:52:42 -0500472
Patrick Williams20f38712022-12-08 06:18:26 -0600473 def protocol_telnet(self, target_type, sub_type):
Peter D Phan5963d632021-07-12 09:58:55 -0500474 r"""
475 Perform actions using telnet protocol.
476 Description of argument(s):
George Keishingf5a57502021-07-22 16:43:47 -0500477 target_type OS Type of remote host.
Peter D Phan5963d632021-07-12 09:58:55 -0500478 """
Patrick Williams20f38712022-12-08 06:18:26 -0600479 self.logger.info(
480 "\n\t[Run] Executing commands on %s using %s"
481 % (self.hostname, "TELNET")
482 )
Peter D Phan5963d632021-07-12 09:58:55 -0500483 telnet_files_saved = []
484 progress_counter = 0
Patrick Williams20f38712022-12-08 06:18:26 -0600485 list_of_commands = self.ffdc_actions[target_type][sub_type]["COMMANDS"]
Peter D Phan5963d632021-07-12 09:58:55 -0500486 for index, each_cmd in enumerate(list_of_commands, start=0):
487 command_txt, command_timeout = self.unpack_command(each_cmd)
Patrick Williams20f38712022-12-08 06:18:26 -0600488 result = self.telnet_remoteclient.execute_command(
489 command_txt, command_timeout
490 )
Peter D Phan5963d632021-07-12 09:58:55 -0500491 if result:
492 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600493 targ_file = self.ffdc_actions[target_type][sub_type][
494 "FILES"
495 ][index]
Peter D Phan5963d632021-07-12 09:58:55 -0500496 except IndexError:
Peter D Phane86d9a52021-07-15 10:42:25 -0500497 targ_file = command_txt
498 self.logger.warning(
Patrick Williams20f38712022-12-08 06:18:26 -0600499 "\n\t[WARN] Missing filename to store data from"
500 " telnet %s." % each_cmd
501 )
502 self.logger.warning(
503 "\t[WARN] Data will be stored in %s." % targ_file
504 )
505 targ_file_with_path = (
506 self.ffdc_dir_path + self.ffdc_prefix + targ_file
507 )
Peter D Phan5963d632021-07-12 09:58:55 -0500508 # Creates a new file
Patrick Williams20f38712022-12-08 06:18:26 -0600509 with open(targ_file_with_path, "w") as fp:
Peter D Phan5963d632021-07-12 09:58:55 -0500510 fp.write(result)
511 fp.close
512 telnet_files_saved.append(targ_file)
513 progress_counter += 1
514 self.print_progress(progress_counter)
Peter D Phane86d9a52021-07-15 10:42:25 -0500515 self.logger.info("\n\t[Run] Commands execution completed.\t\t [OK]")
Peter D Phan5963d632021-07-12 09:58:55 -0500516 for file in telnet_files_saved:
Peter D Phane86d9a52021-07-15 10:42:25 -0500517 self.logger.info("\n\t\tSuccessfully save file " + file + ".")
Peter D Phan5963d632021-07-12 09:58:55 -0500518
Patrick Williams20f38712022-12-08 06:18:26 -0600519 def protocol_execute(self, protocol, target_type, sub_type):
Peter D Phan0c669772021-06-24 13:52:42 -0500520 r"""
George Keishing506b0582021-07-27 09:31:22 -0500521 Perform actions for a given protocol.
Peter D Phan0c669772021-06-24 13:52:42 -0500522
523 Description of argument(s):
George Keishing506b0582021-07-27 09:31:22 -0500524 protocol Protocol to execute.
George Keishingf5a57502021-07-22 16:43:47 -0500525 target_type OS Type of remote host.
George Keishing6ea92b02021-07-01 11:20:50 -0500526 sub_type Group type of commands.
Peter D Phan0c669772021-06-24 13:52:42 -0500527 """
528
Patrick Williams20f38712022-12-08 06:18:26 -0600529 self.logger.info(
530 "\n\t[Run] Executing commands to %s using %s"
531 % (self.hostname, protocol)
532 )
George Keishing506b0582021-07-27 09:31:22 -0500533 executed_files_saved = []
George Keishingeafba182021-06-29 13:44:58 -0500534 progress_counter = 0
Patrick Williams20f38712022-12-08 06:18:26 -0600535 list_of_cmd = self.get_command_list(
536 self.ffdc_actions[target_type][sub_type]
537 )
George Keishingeafba182021-06-29 13:44:58 -0500538 for index, each_cmd in enumerate(list_of_cmd, start=0):
George Keishingcaa97e62021-08-03 14:00:09 -0500539 plugin_call = False
George Keishingb97a9042021-07-29 07:41:20 -0500540 if isinstance(each_cmd, dict):
Patrick Williams20f38712022-12-08 06:18:26 -0600541 if "plugin" in each_cmd:
George Keishing1e7b0182021-08-06 14:05:54 -0500542 # If the error is set and plugin explicitly
543 # requested to skip execution on error..
Patrick Williams20f38712022-12-08 06:18:26 -0600544 if plugin_error_dict[
545 "exit_on_error"
546 ] and self.plugin_error_check(each_cmd["plugin"]):
547 self.logger.info(
548 "\n\t[PLUGIN-ERROR] exit_on_error: %s"
549 % plugin_error_dict["exit_on_error"]
550 )
551 self.logger.info(
552 "\t[PLUGIN-SKIP] %s" % each_cmd["plugin"][0]
553 )
George Keishing1e7b0182021-08-06 14:05:54 -0500554 continue
George Keishingcaa97e62021-08-03 14:00:09 -0500555 plugin_call = True
George Keishingb97a9042021-07-29 07:41:20 -0500556 # call the plugin
557 self.logger.info("\n\t[PLUGIN-START]")
Patrick Williams20f38712022-12-08 06:18:26 -0600558 result = self.execute_plugin_block(each_cmd["plugin"])
George Keishingb97a9042021-07-29 07:41:20 -0500559 self.logger.info("\t[PLUGIN-END]\n")
George Keishingb97a9042021-07-29 07:41:20 -0500560 else:
George Keishing2b83e042021-08-03 12:56:11 -0500561 each_cmd = self.yaml_env_and_plugin_vars_populate(each_cmd)
George Keishingb97a9042021-07-29 07:41:20 -0500562
George Keishingcaa97e62021-08-03 14:00:09 -0500563 if not plugin_call:
564 result = self.run_tool_cmd(each_cmd)
George Keishingeafba182021-06-29 13:44:58 -0500565 if result:
566 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600567 file_name = self.get_file_list(
568 self.ffdc_actions[target_type][sub_type]
569 )[index]
George Keishingb97a9042021-07-29 07:41:20 -0500570 # If file is specified as None.
George Keishing0581cb02021-08-05 15:08:58 -0500571 if file_name == "None":
George Keishingb97a9042021-07-29 07:41:20 -0500572 continue
Patrick Williams20f38712022-12-08 06:18:26 -0600573 targ_file = self.yaml_env_and_plugin_vars_populate(
574 file_name
575 )
George Keishingeafba182021-06-29 13:44:58 -0500576 except IndexError:
Patrick Williams20f38712022-12-08 06:18:26 -0600577 targ_file = each_cmd.split("/")[-1]
George Keishing506b0582021-07-27 09:31:22 -0500578 self.logger.warning(
Patrick Williams20f38712022-12-08 06:18:26 -0600579 "\n\t[WARN] Missing filename to store data from %s."
580 % each_cmd
581 )
582 self.logger.warning(
583 "\t[WARN] Data will be stored in %s." % targ_file
584 )
George Keishingeafba182021-06-29 13:44:58 -0500585
Patrick Williams20f38712022-12-08 06:18:26 -0600586 targ_file_with_path = (
587 self.ffdc_dir_path + self.ffdc_prefix + targ_file
588 )
George Keishingeafba182021-06-29 13:44:58 -0500589
590 # Creates a new file
Patrick Williams20f38712022-12-08 06:18:26 -0600591 with open(targ_file_with_path, "w") as fp:
George Keishing91308ea2021-08-10 14:43:15 -0500592 if isinstance(result, dict):
593 fp.write(json.dumps(result))
594 else:
595 fp.write(result)
George Keishingeafba182021-06-29 13:44:58 -0500596 fp.close
George Keishing506b0582021-07-27 09:31:22 -0500597 executed_files_saved.append(targ_file)
George Keishingeafba182021-06-29 13:44:58 -0500598
599 progress_counter += 1
600 self.print_progress(progress_counter)
601
Peter D Phane86d9a52021-07-15 10:42:25 -0500602 self.logger.info("\n\t[Run] Commands execution completed.\t\t [OK]")
George Keishingeafba182021-06-29 13:44:58 -0500603
George Keishing506b0582021-07-27 09:31:22 -0500604 for file in executed_files_saved:
Peter D Phane86d9a52021-07-15 10:42:25 -0500605 self.logger.info("\n\t\tSuccessfully save file " + file + ".")
George Keishingeafba182021-06-29 13:44:58 -0500606
Patrick Williams20f38712022-12-08 06:18:26 -0600607 def collect_and_copy_ffdc(
608 self, ffdc_actions_for_target_type, form_filename=False
609 ):
Peter D Phan04aca3b2021-06-21 10:37:18 -0500610 r"""
611 Send commands in ffdc_config file to targeted system.
612
613 Description of argument(s):
George Keishingf5a57502021-07-22 16:43:47 -0500614 ffdc_actions_for_target_type commands and files for the selected remote host type.
Peter D Phan2b8052d2021-06-22 10:55:41 -0500615 form_filename if true, pre-pend self.target_type to filename
Peter D Phan04aca3b2021-06-21 10:37:18 -0500616 """
617
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500618 # Executing commands, if any
Patrick Williams20f38712022-12-08 06:18:26 -0600619 self.ssh_execute_ffdc_commands(
620 ffdc_actions_for_target_type, form_filename
621 )
Peter D Phan04aca3b2021-06-21 10:37:18 -0500622
Peter D Phan3beb02e2021-07-06 13:25:17 -0500623 # Copying files
Peter D Phan5963d632021-07-12 09:58:55 -0500624 if self.ssh_remoteclient.scpclient:
Patrick Williams20f38712022-12-08 06:18:26 -0600625 self.logger.info(
626 "\n\n\tCopying FFDC files from remote system %s.\n"
627 % self.hostname
628 )
Peter D Phan2b8052d2021-06-22 10:55:41 -0500629
Peter D Phan04aca3b2021-06-21 10:37:18 -0500630 # Retrieving files from target system
George Keishingf5a57502021-07-22 16:43:47 -0500631 list_of_files = self.get_file_list(ffdc_actions_for_target_type)
Patrick Williams20f38712022-12-08 06:18:26 -0600632 self.scp_ffdc(
633 self.ffdc_dir_path,
634 self.ffdc_prefix,
635 form_filename,
636 list_of_files,
637 )
Peter D Phan04aca3b2021-06-21 10:37:18 -0500638 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600639 self.logger.info(
640 "\n\n\tSkip copying FFDC files from remote system %s.\n"
641 % self.hostname
642 )
Peter D Phan04aca3b2021-06-21 10:37:18 -0500643
Patrick Williams20f38712022-12-08 06:18:26 -0600644 def get_command_list(self, ffdc_actions_for_target_type):
Peter D Phanbabf2962021-07-07 11:24:40 -0500645 r"""
646 Fetch list of commands from configuration file
647
648 Description of argument(s):
George Keishingf5a57502021-07-22 16:43:47 -0500649 ffdc_actions_for_target_type commands and files for the selected remote host type.
Peter D Phanbabf2962021-07-07 11:24:40 -0500650 """
651 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600652 list_of_commands = ffdc_actions_for_target_type["COMMANDS"]
Peter D Phanbabf2962021-07-07 11:24:40 -0500653 except KeyError:
654 list_of_commands = []
655 return list_of_commands
656
Patrick Williams20f38712022-12-08 06:18:26 -0600657 def get_file_list(self, ffdc_actions_for_target_type):
Peter D Phanbabf2962021-07-07 11:24:40 -0500658 r"""
659 Fetch list of commands from configuration file
660
661 Description of argument(s):
George Keishingf5a57502021-07-22 16:43:47 -0500662 ffdc_actions_for_target_type commands and files for the selected remote host type.
Peter D Phanbabf2962021-07-07 11:24:40 -0500663 """
664 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600665 list_of_files = ffdc_actions_for_target_type["FILES"]
Peter D Phanbabf2962021-07-07 11:24:40 -0500666 except KeyError:
667 list_of_files = []
668 return list_of_files
669
Patrick Williams20f38712022-12-08 06:18:26 -0600670 def unpack_command(self, command):
Peter D Phan5963d632021-07-12 09:58:55 -0500671 r"""
672 Unpack command from config file
673
674 Description of argument(s):
675 command Command from config file.
676 """
677 if isinstance(command, dict):
678 command_txt = next(iter(command))
679 command_timeout = next(iter(command.values()))
680 elif isinstance(command, str):
681 command_txt = command
682 # Default command timeout 60 seconds
683 command_timeout = 60
684
685 return command_txt, command_timeout
686
Patrick Williams20f38712022-12-08 06:18:26 -0600687 def ssh_execute_ffdc_commands(
688 self, ffdc_actions_for_target_type, form_filename=False
689 ):
Peter D Phan3beb02e2021-07-06 13:25:17 -0500690 r"""
691 Send commands in ffdc_config file to targeted system.
692
693 Description of argument(s):
George Keishingf5a57502021-07-22 16:43:47 -0500694 ffdc_actions_for_target_type commands and files for the selected remote host type.
Peter D Phan3beb02e2021-07-06 13:25:17 -0500695 form_filename if true, pre-pend self.target_type to filename
696 """
Patrick Williams20f38712022-12-08 06:18:26 -0600697 self.logger.info(
698 "\n\t[Run] Executing commands on %s using %s"
699 % (self.hostname, ffdc_actions_for_target_type["PROTOCOL"][0])
700 )
Peter D Phan3beb02e2021-07-06 13:25:17 -0500701
George Keishingf5a57502021-07-22 16:43:47 -0500702 list_of_commands = self.get_command_list(ffdc_actions_for_target_type)
Peter D Phan3beb02e2021-07-06 13:25:17 -0500703 # If command list is empty, returns
704 if not list_of_commands:
705 return
706
707 progress_counter = 0
708 for command in list_of_commands:
Peter D Phan5963d632021-07-12 09:58:55 -0500709 command_txt, command_timeout = self.unpack_command(command)
Peter D Phan3beb02e2021-07-06 13:25:17 -0500710
711 if form_filename:
712 command_txt = str(command_txt % self.target_type)
713
Patrick Williams20f38712022-12-08 06:18:26 -0600714 (
715 cmd_exit_code,
716 err,
717 response,
718 ) = self.ssh_remoteclient.execute_command(
719 command_txt, command_timeout
720 )
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500721
722 if cmd_exit_code:
723 self.logger.warning(
Patrick Williams20f38712022-12-08 06:18:26 -0600724 "\n\t\t[WARN] %s exits with code %s."
725 % (command_txt, str(cmd_exit_code))
726 )
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500727 self.logger.warning("\t\t[WARN] %s " % err)
Peter D Phanbabf2962021-07-07 11:24:40 -0500728
Peter D Phan3beb02e2021-07-06 13:25:17 -0500729 progress_counter += 1
730 self.print_progress(progress_counter)
731
Peter D Phane86d9a52021-07-15 10:42:25 -0500732 self.logger.info("\n\t[Run] Commands execution completed.\t\t [OK]")
Peter D Phan3beb02e2021-07-06 13:25:17 -0500733
Patrick Williams20f38712022-12-08 06:18:26 -0600734 def group_copy(self, ffdc_actions_for_target_type):
Peter D Phan56429a62021-06-23 08:38:29 -0500735 r"""
736 scp group of files (wild card) from remote host.
737
738 Description of argument(s):
George Keishingf5a57502021-07-22 16:43:47 -0500739 fdc_actions_for_target_type commands and files for the selected remote host type.
Peter D Phan56429a62021-06-23 08:38:29 -0500740 """
Peter D Phan3beb02e2021-07-06 13:25:17 -0500741
Peter D Phan5963d632021-07-12 09:58:55 -0500742 if self.ssh_remoteclient.scpclient:
Patrick Williams20f38712022-12-08 06:18:26 -0600743 self.logger.info(
744 "\n\tCopying files from remote system %s via SCP.\n"
745 % self.hostname
746 )
Peter D Phan56429a62021-06-23 08:38:29 -0500747
Patrick Williams20f38712022-12-08 06:18:26 -0600748 list_of_commands = self.get_command_list(
749 ffdc_actions_for_target_type
750 )
Peter D Phanbabf2962021-07-07 11:24:40 -0500751 # If command list is empty, returns
752 if not list_of_commands:
753 return
Peter D Phan56429a62021-06-23 08:38:29 -0500754
Peter D Phanbabf2962021-07-07 11:24:40 -0500755 for command in list_of_commands:
756 try:
George Keishingb4540e72021-08-02 13:48:46 -0500757 command = self.yaml_env_and_plugin_vars_populate(command)
Peter D Phanbabf2962021-07-07 11:24:40 -0500758 except IndexError:
George Keishingb4540e72021-08-02 13:48:46 -0500759 self.logger.error("\t\tInvalid command %s" % command)
Peter D Phanbabf2962021-07-07 11:24:40 -0500760 continue
761
Patrick Williams20f38712022-12-08 06:18:26 -0600762 (
763 cmd_exit_code,
764 err,
765 response,
766 ) = self.ssh_remoteclient.execute_command(command)
Peter D Phanbabf2962021-07-07 11:24:40 -0500767
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500768 # If file does not exist, code take no action.
769 # cmd_exit_code is ignored for this scenario.
Peter D Phan56429a62021-06-23 08:38:29 -0500770 if response:
Patrick Williams20f38712022-12-08 06:18:26 -0600771 scp_result = self.ssh_remoteclient.scp_file_from_remote(
772 response.split("\n"), self.ffdc_dir_path
773 )
Peter D Phan56429a62021-06-23 08:38:29 -0500774 if scp_result:
Patrick Williams20f38712022-12-08 06:18:26 -0600775 self.logger.info(
776 "\t\tSuccessfully copied from "
777 + self.hostname
778 + ":"
779 + command
780 )
Peter D Phan56429a62021-06-23 08:38:29 -0500781 else:
George Keishinga56e87b2021-08-06 00:24:19 -0500782 self.logger.info("\t\t%s has no result" % command)
Peter D Phan56429a62021-06-23 08:38:29 -0500783
784 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600785 self.logger.info(
786 "\n\n\tSkip copying files from remote system %s.\n"
787 % self.hostname
788 )
Peter D Phan56429a62021-06-23 08:38:29 -0500789
Patrick Williams20f38712022-12-08 06:18:26 -0600790 def scp_ffdc(
791 self,
792 targ_dir_path,
793 targ_file_prefix,
794 form_filename,
795 file_list=None,
796 quiet=None,
797 ):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500798 r"""
799 SCP all files in file_dict to the indicated directory on the local system.
800
801 Description of argument(s):
802 targ_dir_path The path of the directory to receive the files.
George Keishinge16f1582022-12-15 07:32:21 -0600803 targ_file_prefix Prefix which will be prepended to each
Peter D Phan72ce6b82021-06-03 06:18:26 -0500804 target file's name.
805 file_dict A dictionary of files to scp from targeted system to this system
806
807 """
808
Peter D Phan72ce6b82021-06-03 06:18:26 -0500809 progress_counter = 0
810 for filename in file_list:
Peter D Phan2b8052d2021-06-22 10:55:41 -0500811 if form_filename:
812 filename = str(filename % self.target_type)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500813 source_file_path = filename
Patrick Williams20f38712022-12-08 06:18:26 -0600814 targ_file_path = (
815 targ_dir_path + targ_file_prefix + filename.split("/")[-1]
816 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500817
Peter D Phanbabf2962021-07-07 11:24:40 -0500818 # If source file name contains wild card, copy filename as is.
Patrick Williams20f38712022-12-08 06:18:26 -0600819 if "*" in source_file_path:
820 scp_result = self.ssh_remoteclient.scp_file_from_remote(
821 source_file_path, self.ffdc_dir_path
822 )
Peter D Phanbabf2962021-07-07 11:24:40 -0500823 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600824 scp_result = self.ssh_remoteclient.scp_file_from_remote(
825 source_file_path, targ_file_path
826 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500827
828 if not quiet:
829 if scp_result:
Peter D Phane86d9a52021-07-15 10:42:25 -0500830 self.logger.info(
Patrick Williams20f38712022-12-08 06:18:26 -0600831 "\t\tSuccessfully copied from "
832 + self.hostname
833 + ":"
834 + source_file_path
835 + ".\n"
836 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500837 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500838 self.logger.info(
Patrick Williams20f38712022-12-08 06:18:26 -0600839 "\t\tFail to copy from "
840 + self.hostname
841 + ":"
842 + source_file_path
843 + ".\n"
844 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500845 else:
846 progress_counter += 1
847 self.print_progress(progress_counter)
848
Peter D Phan5e56f522021-12-20 13:19:41 -0600849 def set_ffdc_default_store_path(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500850 r"""
851 Set a default value for self.ffdc_dir_path and self.ffdc_prefix.
852 Collected ffdc file will be stored in dir /self.location/hostname_timestr/.
853 Individual ffdc file will have timestr_filename.
854
855 Description of class variables:
856 self.ffdc_dir_path The dir path where collected ffdc data files should be put.
857
858 self.ffdc_prefix The prefix to be given to each ffdc file name.
859
860 """
861
862 timestr = time.strftime("%Y%m%d-%H%M%S")
Patrick Williams20f38712022-12-08 06:18:26 -0600863 self.ffdc_dir_path = (
864 self.location + "/" + self.hostname + "_" + timestr + "/"
865 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500866 self.ffdc_prefix = timestr + "_"
867 self.validate_local_store(self.ffdc_dir_path)
868
Peter D Phan5e56f522021-12-20 13:19:41 -0600869 # Need to verify local store path exists prior to instantiate this class.
870 # This class method is used to share the same code between CLI input parm
871 # and Robot Framework "${EXECDIR}/logs" before referencing this class.
872 @classmethod
873 def validate_local_store(cls, dir_path):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500874 r"""
875 Ensure path exists to store FFDC files locally.
876
877 Description of variable:
878 dir_path The dir path where collected ffdc data files will be stored.
879
880 """
881
882 if not os.path.exists(dir_path):
883 try:
George Keishing7b3a5132021-07-13 09:24:02 -0500884 os.makedirs(dir_path, 0o755)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500885 except (IOError, OSError) as e:
886 # PermissionError
887 if e.errno == EPERM or e.errno == EACCES:
Peter D Phane86d9a52021-07-15 10:42:25 -0500888 self.logger.error(
Patrick Williams20f38712022-12-08 06:18:26 -0600889 "\tERROR: os.makedirs %s failed with"
890 " PermissionError.\n" % dir_path
891 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500892 else:
Peter D Phane86d9a52021-07-15 10:42:25 -0500893 self.logger.error(
Patrick Williams20f38712022-12-08 06:18:26 -0600894 "\tERROR: os.makedirs %s failed with %s.\n"
895 % (dir_path, e.strerror)
896 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500897 sys.exit(-1)
898
899 def print_progress(self, progress):
900 r"""
901 Print activity progress +
902
903 Description of variable:
904 progress Progress counter.
905
906 """
907
908 sys.stdout.write("\r\t" + "+" * progress)
909 sys.stdout.flush()
Patrick Williams20f38712022-12-08 06:18:26 -0600910 time.sleep(0.1)
Peter D Phan0c669772021-06-24 13:52:42 -0500911
912 def verify_redfish(self):
913 r"""
914 Verify remote host has redfish service active
915
916 """
Patrick Williams20f38712022-12-08 06:18:26 -0600917 redfish_parm = (
918 "redfishtool -r "
919 + self.hostname
George Keishing7a61aa22023-06-26 13:18:37 +0530920 + ":"
921 + self.port_https
Patrick Williams20f38712022-12-08 06:18:26 -0600922 + " -S Always raw GET /redfish/v1/"
923 )
924 return self.run_tool_cmd(redfish_parm, True)
Peter D Phan0c669772021-06-24 13:52:42 -0500925
George Keishingeafba182021-06-29 13:44:58 -0500926 def verify_ipmi(self):
927 r"""
928 Verify remote host has IPMI LAN service active
929
930 """
Patrick Williams20f38712022-12-08 06:18:26 -0600931 if self.target_type == "OPENBMC":
932 ipmi_parm = (
933 "ipmitool -I lanplus -C 17 -U "
934 + self.username
935 + " -P "
936 + self.password
937 + " -H "
938 + self.hostname
George Keishinge8a41752023-06-22 21:42:47 +0530939 + " -p "
940 + str(self.port_ipmi)
Patrick Williams20f38712022-12-08 06:18:26 -0600941 + " power status"
942 )
George Keishing484f8242021-07-27 01:42:02 -0500943 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600944 ipmi_parm = (
945 "ipmitool -I lanplus -P "
946 + self.password
947 + " -H "
948 + self.hostname
George Keishinge8a41752023-06-22 21:42:47 +0530949 + " -p "
950 + str(self.port_ipmi)
Patrick Williams20f38712022-12-08 06:18:26 -0600951 + " power status"
952 )
George Keishing484f8242021-07-27 01:42:02 -0500953
Patrick Williams20f38712022-12-08 06:18:26 -0600954 return self.run_tool_cmd(ipmi_parm, True)
George Keishingeafba182021-06-29 13:44:58 -0500955
Patrick Williams20f38712022-12-08 06:18:26 -0600956 def run_tool_cmd(self, parms_string, quiet=False):
George Keishingeafba182021-06-29 13:44:58 -0500957 r"""
George Keishing506b0582021-07-27 09:31:22 -0500958 Run CLI standard tool or scripts.
George Keishingeafba182021-06-29 13:44:58 -0500959
960 Description of variable:
George Keishing506b0582021-07-27 09:31:22 -0500961 parms_string tool command options.
962 quiet do not print tool error message if True
George Keishingeafba182021-06-29 13:44:58 -0500963 """
964
Patrick Williams20f38712022-12-08 06:18:26 -0600965 result = subprocess.run(
966 [parms_string],
967 stdout=subprocess.PIPE,
968 stderr=subprocess.PIPE,
969 shell=True,
970 universal_newlines=True,
971 )
George Keishingeafba182021-06-29 13:44:58 -0500972
973 if result.stderr and not quiet:
Patrick Williams20f38712022-12-08 06:18:26 -0600974 self.logger.error("\n\t\tERROR with %s " % parms_string)
975 self.logger.error("\t\t" + result.stderr)
George Keishingeafba182021-06-29 13:44:58 -0500976
977 return result.stdout
George Keishing04d29102021-07-16 02:05:57 -0500978
George Keishingf5a57502021-07-22 16:43:47 -0500979 def verify_protocol(self, protocol_list):
980 r"""
981 Perform protocol working check.
982
983 Description of argument(s):
984 protocol_list List of protocol.
985 """
986
987 tmp_list = []
988 if self.target_is_pingable():
989 tmp_list.append("SHELL")
990
991 for protocol in protocol_list:
Patrick Williams20f38712022-12-08 06:18:26 -0600992 if self.remote_protocol != "ALL":
George Keishingf5a57502021-07-22 16:43:47 -0500993 if self.remote_protocol != protocol:
994 continue
995
996 # Only check SSH/SCP once for both protocols
Patrick Williams20f38712022-12-08 06:18:26 -0600997 if (
998 protocol == "SSH"
999 or protocol == "SCP"
1000 and protocol not in tmp_list
1001 ):
George Keishingf5a57502021-07-22 16:43:47 -05001002 if self.ssh_to_target_system():
George Keishingaa638702021-07-26 11:48:28 -05001003 # Add only what user asked.
Patrick Williams20f38712022-12-08 06:18:26 -06001004 if self.remote_protocol != "ALL":
George Keishingaa638702021-07-26 11:48:28 -05001005 tmp_list.append(self.remote_protocol)
1006 else:
Patrick Williams20f38712022-12-08 06:18:26 -06001007 tmp_list.append("SSH")
1008 tmp_list.append("SCP")
George Keishingf5a57502021-07-22 16:43:47 -05001009
Patrick Williams20f38712022-12-08 06:18:26 -06001010 if protocol == "TELNET":
George Keishingf5a57502021-07-22 16:43:47 -05001011 if self.telnet_to_target_system():
1012 tmp_list.append(protocol)
1013
Patrick Williams20f38712022-12-08 06:18:26 -06001014 if protocol == "REDFISH":
George Keishingf5a57502021-07-22 16:43:47 -05001015 if self.verify_redfish():
1016 tmp_list.append(protocol)
Patrick Williams20f38712022-12-08 06:18:26 -06001017 self.logger.info(
1018 "\n\t[Check] %s Redfish Service.\t\t [OK]"
1019 % self.hostname
1020 )
George Keishingf5a57502021-07-22 16:43:47 -05001021 else:
Patrick Williams20f38712022-12-08 06:18:26 -06001022 self.logger.info(
1023 "\n\t[Check] %s Redfish Service.\t\t [NOT AVAILABLE]"
1024 % self.hostname
1025 )
George Keishingf5a57502021-07-22 16:43:47 -05001026
Patrick Williams20f38712022-12-08 06:18:26 -06001027 if protocol == "IPMI":
George Keishingf5a57502021-07-22 16:43:47 -05001028 if self.verify_ipmi():
1029 tmp_list.append(protocol)
Patrick Williams20f38712022-12-08 06:18:26 -06001030 self.logger.info(
1031 "\n\t[Check] %s IPMI LAN Service.\t\t [OK]"
1032 % self.hostname
1033 )
George Keishingf5a57502021-07-22 16:43:47 -05001034 else:
Patrick Williams20f38712022-12-08 06:18:26 -06001035 self.logger.info(
1036 "\n\t[Check] %s IPMI LAN Service.\t\t [NOT AVAILABLE]"
1037 % self.hostname
1038 )
George Keishingf5a57502021-07-22 16:43:47 -05001039
1040 return tmp_list
George Keishinge1686752021-07-27 12:55:28 -05001041
1042 def load_env(self):
1043 r"""
1044 Perform protocol working check.
1045
1046 """
1047 # This is for the env vars a user can use in YAML to load it at runtime.
1048 # Example YAML:
1049 # -COMMANDS:
1050 # - my_command ${hostname} ${username} ${password}
Patrick Williams20f38712022-12-08 06:18:26 -06001051 os.environ["hostname"] = self.hostname
1052 os.environ["username"] = self.username
1053 os.environ["password"] = self.password
George Keishing7a61aa22023-06-26 13:18:37 +05301054 os.environ["port_ssh"] = self.port_ssh
George Keishinge8a41752023-06-22 21:42:47 +05301055 os.environ["port_https"] = self.port_https
1056 os.environ["port_ipmi"] = self.port_ipmi
George Keishinge1686752021-07-27 12:55:28 -05001057
1058 # Append default Env.
Patrick Williams20f38712022-12-08 06:18:26 -06001059 self.env_dict["hostname"] = self.hostname
1060 self.env_dict["username"] = self.username
1061 self.env_dict["password"] = self.password
George Keishing7a61aa22023-06-26 13:18:37 +05301062 self.env_dict["port_ssh"] = self.port_ssh
George Keishinge8a41752023-06-22 21:42:47 +05301063 self.env_dict["port_https"] = self.port_https
1064 self.env_dict["port_ipmi"] = self.port_ipmi
George Keishinge1686752021-07-27 12:55:28 -05001065
1066 try:
1067 tmp_env_dict = {}
1068 if self.env_vars:
1069 tmp_env_dict = json.loads(self.env_vars)
1070 # Export ENV vars default.
1071 for key, value in tmp_env_dict.items():
1072 os.environ[key] = value
1073 self.env_dict[key] = str(value)
1074
1075 if self.econfig:
Patrick Williams20f38712022-12-08 06:18:26 -06001076 with open(self.econfig, "r") as file:
George Keishinge9b23d32021-08-13 12:57:58 -05001077 try:
Yunyun Linf87cc0a2022-06-08 16:57:04 -07001078 tmp_env_dict = yaml.load(file, Loader=yaml.SafeLoader)
George Keishinge9b23d32021-08-13 12:57:58 -05001079 except yaml.YAMLError as e:
1080 self.logger.error(e)
1081 sys.exit(-1)
George Keishinge1686752021-07-27 12:55:28 -05001082 # Export ENV vars.
Patrick Williams20f38712022-12-08 06:18:26 -06001083 for key, value in tmp_env_dict["env_params"].items():
George Keishinge1686752021-07-27 12:55:28 -05001084 os.environ[key] = str(value)
1085 self.env_dict[key] = str(value)
1086 except json.decoder.JSONDecodeError as e:
1087 self.logger.error("\n\tERROR: %s " % e)
1088 sys.exit(-1)
1089
1090 # This to mask the password from displaying on the console.
1091 mask_dict = self.env_dict.copy()
1092 for k, v in mask_dict.items():
1093 if k.lower().find("password") != -1:
1094 hidden_text = []
1095 hidden_text.append(v)
Patrick Williams20f38712022-12-08 06:18:26 -06001096 password_regex = (
1097 "(" + "|".join([re.escape(x) for x in hidden_text]) + ")"
1098 )
George Keishinge1686752021-07-27 12:55:28 -05001099 mask_dict[k] = re.sub(password_regex, "********", v)
1100
1101 self.logger.info(json.dumps(mask_dict, indent=8, sort_keys=False))
George Keishingb97a9042021-07-29 07:41:20 -05001102
1103 def execute_python_eval(self, eval_string):
1104 r"""
George Keishing9348b402021-08-13 12:22:35 -05001105 Execute qualified python function string using eval.
George Keishingb97a9042021-07-29 07:41:20 -05001106
1107 Description of argument(s):
1108 eval_string Execute the python object.
1109
1110 Example:
1111 eval(plugin.foo_func.foo_func(10))
1112 """
1113 try:
George Keishingdda48ce2021-08-12 07:02:27 -05001114 self.logger.info("\tExecuting plugin func()")
1115 self.logger.debug("\tCall func: %s" % eval_string)
George Keishingb97a9042021-07-29 07:41:20 -05001116 result = eval(eval_string)
1117 self.logger.info("\treturn: %s" % str(result))
Patrick Williams20f38712022-12-08 06:18:26 -06001118 except (
1119 ValueError,
1120 SyntaxError,
1121 NameError,
1122 AttributeError,
1123 TypeError,
1124 ) as e:
George Keishing1e7b0182021-08-06 14:05:54 -05001125 self.logger.error("\tERROR: execute_python_eval: %s" % e)
1126 # Set the plugin error state.
Patrick Williams20f38712022-12-08 06:18:26 -06001127 plugin_error_dict["exit_on_error"] = True
George Keishing73b95d12021-08-13 14:30:52 -05001128 self.logger.info("\treturn: PLUGIN_EVAL_ERROR")
Patrick Williams20f38712022-12-08 06:18:26 -06001129 return "PLUGIN_EVAL_ERROR"
George Keishingb97a9042021-07-29 07:41:20 -05001130
1131 return result
1132
1133 def execute_plugin_block(self, plugin_cmd_list):
1134 r"""
Peter D Phan5e56f522021-12-20 13:19:41 -06001135 Pack the plugin command to qualifed python string object.
George Keishingb97a9042021-07-29 07:41:20 -05001136
1137 Description of argument(s):
1138 plugin_list_dict Plugin block read from YAML
1139 [{'plugin_name': 'plugin.foo_func.my_func'},
1140 {'plugin_args': [10]}]
1141
1142 Example:
1143 - plugin:
1144 - plugin_name: plugin.foo_func.my_func
1145 - plugin_args:
1146 - arg1
1147 - arg2
1148
1149 - plugin:
1150 - plugin_name: result = plugin.foo_func.my_func
1151 - plugin_args:
1152 - arg1
1153 - arg2
1154
1155 - plugin:
1156 - plugin_name: result1,result2 = plugin.foo_func.my_func
1157 - plugin_args:
1158 - arg1
1159 - arg2
1160 """
1161 try:
Patrick Williams20f38712022-12-08 06:18:26 -06001162 idx = self.key_index_list_dict("plugin_name", plugin_cmd_list)
1163 plugin_name = plugin_cmd_list[idx]["plugin_name"]
George Keishingb97a9042021-07-29 07:41:20 -05001164 # Equal separator means plugin function returns result.
Patrick Williams20f38712022-12-08 06:18:26 -06001165 if " = " in plugin_name:
George Keishingb97a9042021-07-29 07:41:20 -05001166 # Ex. ['result', 'plugin.foo_func.my_func']
Patrick Williams20f38712022-12-08 06:18:26 -06001167 plugin_name_args = plugin_name.split(" = ")
George Keishingb97a9042021-07-29 07:41:20 -05001168 # plugin func return data.
1169 for arg in plugin_name_args:
1170 if arg == plugin_name_args[-1]:
1171 plugin_name = arg
1172 else:
Patrick Williams20f38712022-12-08 06:18:26 -06001173 plugin_resp = arg.split(",")
George Keishingb97a9042021-07-29 07:41:20 -05001174 # ['result1','result2']
1175 for x in plugin_resp:
1176 global_plugin_list.append(x)
1177 global_plugin_dict[x] = ""
1178
1179 # Walk the plugin args ['arg1,'arg2']
1180 # If the YAML plugin statement 'plugin_args' is not declared.
Patrick Williams20f38712022-12-08 06:18:26 -06001181 if any("plugin_args" in d for d in plugin_cmd_list):
1182 idx = self.key_index_list_dict("plugin_args", plugin_cmd_list)
1183 plugin_args = plugin_cmd_list[idx]["plugin_args"]
George Keishingb97a9042021-07-29 07:41:20 -05001184 if plugin_args:
1185 plugin_args = self.yaml_args_populate(plugin_args)
1186 else:
1187 plugin_args = []
1188 else:
1189 plugin_args = self.yaml_args_populate([])
1190
1191 # Pack the args arg1, arg2, .... argn into
1192 # "arg1","arg2","argn" string as params for function.
1193 parm_args_str = self.yaml_args_string(plugin_args)
1194 if parm_args_str:
Patrick Williams20f38712022-12-08 06:18:26 -06001195 plugin_func = plugin_name + "(" + parm_args_str + ")"
George Keishingb97a9042021-07-29 07:41:20 -05001196 else:
Patrick Williams20f38712022-12-08 06:18:26 -06001197 plugin_func = plugin_name + "()"
George Keishingb97a9042021-07-29 07:41:20 -05001198
1199 # Execute plugin function.
1200 if global_plugin_dict:
1201 resp = self.execute_python_eval(plugin_func)
George Keishing9348b402021-08-13 12:22:35 -05001202 # Update plugin vars dict if there is any.
Patrick Williams20f38712022-12-08 06:18:26 -06001203 if resp != "PLUGIN_EVAL_ERROR":
George Keishing73b95d12021-08-13 14:30:52 -05001204 self.response_args_data(resp)
George Keishingb97a9042021-07-29 07:41:20 -05001205 else:
George Keishingcaa97e62021-08-03 14:00:09 -05001206 resp = self.execute_python_eval(plugin_func)
George Keishingb97a9042021-07-29 07:41:20 -05001207 except Exception as e:
George Keishing1e7b0182021-08-06 14:05:54 -05001208 # Set the plugin error state.
Patrick Williams20f38712022-12-08 06:18:26 -06001209 plugin_error_dict["exit_on_error"] = True
George Keishing1e7b0182021-08-06 14:05:54 -05001210 self.logger.error("\tERROR: execute_plugin_block: %s" % e)
George Keishingb97a9042021-07-29 07:41:20 -05001211 pass
1212
George Keishing73b95d12021-08-13 14:30:52 -05001213 # There is a real error executing the plugin function.
Patrick Williams20f38712022-12-08 06:18:26 -06001214 if resp == "PLUGIN_EVAL_ERROR":
George Keishing73b95d12021-08-13 14:30:52 -05001215 return resp
1216
George Keishingde79a9b2021-08-12 16:14:43 -05001217 # Check if plugin_expects_return (int, string, list,dict etc)
Patrick Williams20f38712022-12-08 06:18:26 -06001218 if any("plugin_expects_return" in d for d in plugin_cmd_list):
1219 idx = self.key_index_list_dict(
1220 "plugin_expects_return", plugin_cmd_list
1221 )
1222 plugin_expects = plugin_cmd_list[idx]["plugin_expects_return"]
George Keishingde79a9b2021-08-12 16:14:43 -05001223 if plugin_expects:
1224 if resp:
Patrick Williams20f38712022-12-08 06:18:26 -06001225 if (
1226 self.plugin_expect_type(plugin_expects, resp)
1227 == "INVALID"
1228 ):
George Keishingde79a9b2021-08-12 16:14:43 -05001229 self.logger.error("\tWARN: Plugin error check skipped")
1230 elif not self.plugin_expect_type(plugin_expects, resp):
Patrick Williams20f38712022-12-08 06:18:26 -06001231 self.logger.error(
1232 "\tERROR: Plugin expects return data: %s"
1233 % plugin_expects
1234 )
1235 plugin_error_dict["exit_on_error"] = True
George Keishingde79a9b2021-08-12 16:14:43 -05001236 elif not resp:
Patrick Williams20f38712022-12-08 06:18:26 -06001237 self.logger.error(
1238 "\tERROR: Plugin func failed to return data"
1239 )
1240 plugin_error_dict["exit_on_error"] = True
George Keishingde79a9b2021-08-12 16:14:43 -05001241
1242 return resp
1243
George Keishingb97a9042021-07-29 07:41:20 -05001244 def response_args_data(self, plugin_resp):
1245 r"""
George Keishing9348b402021-08-13 12:22:35 -05001246 Parse the plugin function response and update plugin return variable.
George Keishingb97a9042021-07-29 07:41:20 -05001247
1248 plugin_resp Response data from plugin function.
1249 """
1250 resp_list = []
George Keishing5765f792021-08-02 13:08:53 -05001251 resp_data = ""
George Keishing9348b402021-08-13 12:22:35 -05001252
George Keishingb97a9042021-07-29 07:41:20 -05001253 # There is nothing to update the plugin response.
Patrick Williams20f38712022-12-08 06:18:26 -06001254 if len(global_plugin_list) == 0 or plugin_resp == "None":
George Keishingb97a9042021-07-29 07:41:20 -05001255 return
1256
George Keishing5765f792021-08-02 13:08:53 -05001257 if isinstance(plugin_resp, str):
Patrick Williams20f38712022-12-08 06:18:26 -06001258 resp_data = plugin_resp.strip("\r\n\t")
George Keishing5765f792021-08-02 13:08:53 -05001259 resp_list.append(resp_data)
1260 elif isinstance(plugin_resp, bytes):
Patrick Williams20f38712022-12-08 06:18:26 -06001261 resp_data = str(plugin_resp, "UTF-8").strip("\r\n\t")
George Keishing5765f792021-08-02 13:08:53 -05001262 resp_list.append(resp_data)
1263 elif isinstance(plugin_resp, tuple):
1264 if len(global_plugin_list) == 1:
George Keishingb97a9042021-07-29 07:41:20 -05001265 resp_list.append(plugin_resp)
George Keishing5765f792021-08-02 13:08:53 -05001266 else:
1267 resp_list = list(plugin_resp)
Patrick Williams20f38712022-12-08 06:18:26 -06001268 resp_list = [x.strip("\r\n\t") for x in resp_list]
George Keishingb97a9042021-07-29 07:41:20 -05001269 elif isinstance(plugin_resp, list):
George Keishing5765f792021-08-02 13:08:53 -05001270 if len(global_plugin_list) == 1:
Patrick Williams20f38712022-12-08 06:18:26 -06001271 resp_list.append([x.strip("\r\n\t") for x in plugin_resp])
George Keishing5765f792021-08-02 13:08:53 -05001272 else:
Patrick Williams20f38712022-12-08 06:18:26 -06001273 resp_list = [x.strip("\r\n\t") for x in plugin_resp]
George Keishing5765f792021-08-02 13:08:53 -05001274 elif isinstance(plugin_resp, int) or isinstance(plugin_resp, float):
1275 resp_list.append(plugin_resp)
George Keishingb97a9042021-07-29 07:41:20 -05001276
George Keishing9348b402021-08-13 12:22:35 -05001277 # Iterate if there is a list of plugin return vars to update.
George Keishingb97a9042021-07-29 07:41:20 -05001278 for idx, item in enumerate(resp_list, start=0):
George Keishing9348b402021-08-13 12:22:35 -05001279 # Exit loop, done required loop.
George Keishingb97a9042021-07-29 07:41:20 -05001280 if idx >= len(global_plugin_list):
1281 break
1282 # Find the index of the return func in the list and
1283 # update the global func return dictionary.
1284 try:
1285 dict_idx = global_plugin_list[idx]
1286 global_plugin_dict[dict_idx] = item
1287 except (IndexError, ValueError) as e:
George Keishing1e7b0182021-08-06 14:05:54 -05001288 self.logger.warn("\tWARN: response_args_data: %s" % e)
George Keishingb97a9042021-07-29 07:41:20 -05001289 pass
1290
1291 # Done updating plugin dict irrespective of pass or failed,
George Keishing9348b402021-08-13 12:22:35 -05001292 # clear all the list element for next plugin block execute.
George Keishingb97a9042021-07-29 07:41:20 -05001293 global_plugin_list.clear()
1294
1295 def yaml_args_string(self, plugin_args):
1296 r"""
1297 Pack the args into string.
1298
1299 plugin_args arg list ['arg1','arg2,'argn']
1300 """
Patrick Williams20f38712022-12-08 06:18:26 -06001301 args_str = ""
George Keishingb97a9042021-07-29 07:41:20 -05001302 for args in plugin_args:
1303 if args:
George Keishing0581cb02021-08-05 15:08:58 -05001304 if isinstance(args, (int, float)):
George Keishingb97a9042021-07-29 07:41:20 -05001305 args_str += str(args)
George Keishing0581cb02021-08-05 15:08:58 -05001306 elif args in global_plugin_type_list:
1307 args_str += str(global_plugin_dict[args])
George Keishingb97a9042021-07-29 07:41:20 -05001308 else:
Patrick Williams20f38712022-12-08 06:18:26 -06001309 args_str += '"' + str(args.strip("\r\n\t")) + '"'
George Keishingb97a9042021-07-29 07:41:20 -05001310 # Skip last list element.
1311 if args != plugin_args[-1]:
1312 args_str += ","
1313 return args_str
1314
1315 def yaml_args_populate(self, yaml_arg_list):
1316 r"""
George Keishing9348b402021-08-13 12:22:35 -05001317 Decode env and plugin vars and populate.
George Keishingb97a9042021-07-29 07:41:20 -05001318
1319 Description of argument(s):
1320 yaml_arg_list arg list read from YAML
1321
1322 Example:
1323 - plugin_args:
1324 - arg1
1325 - arg2
1326
1327 yaml_arg_list: [arg2, arg2]
1328 """
1329 # Get the env loaded keys as list ['hostname', 'username', 'password'].
1330 env_vars_list = list(self.env_dict)
1331
1332 if isinstance(yaml_arg_list, list):
1333 tmp_list = []
1334 for arg in yaml_arg_list:
George Keishing0581cb02021-08-05 15:08:58 -05001335 if isinstance(arg, (int, float)):
George Keishingb97a9042021-07-29 07:41:20 -05001336 tmp_list.append(arg)
1337 continue
1338 elif isinstance(arg, str):
1339 arg_str = self.yaml_env_and_plugin_vars_populate(str(arg))
1340 tmp_list.append(arg_str)
1341 else:
1342 tmp_list.append(arg)
1343
1344 # return populated list.
1345 return tmp_list
1346
1347 def yaml_env_and_plugin_vars_populate(self, yaml_arg_str):
1348 r"""
George Keishing9348b402021-08-13 12:22:35 -05001349 Update ${MY_VAR} and plugin vars.
George Keishingb97a9042021-07-29 07:41:20 -05001350
1351 Description of argument(s):
George Keishing9348b402021-08-13 12:22:35 -05001352 yaml_arg_str arg string read from YAML.
George Keishingb97a9042021-07-29 07:41:20 -05001353
1354 Example:
1355 - cat ${MY_VAR}
1356 - ls -AX my_plugin_var
1357 """
George Keishing9348b402021-08-13 12:22:35 -05001358 # Parse the string for env vars ${env_vars}.
George Keishingb97a9042021-07-29 07:41:20 -05001359 try:
1360 # Example, list of matching env vars ['username', 'password', 'hostname']
1361 # Extra escape \ for special symbols. '\$\{([^\}]+)\}' works good.
Patrick Williams20f38712022-12-08 06:18:26 -06001362 var_name_regex = "\\$\\{([^\\}]+)\\}"
George Keishingb97a9042021-07-29 07:41:20 -05001363 env_var_names_list = re.findall(var_name_regex, yaml_arg_str)
1364 for var in env_var_names_list:
1365 env_var = os.environ[var]
Patrick Williams20f38712022-12-08 06:18:26 -06001366 env_replace = "${" + var + "}"
George Keishingb97a9042021-07-29 07:41:20 -05001367 yaml_arg_str = yaml_arg_str.replace(env_replace, env_var)
1368 except Exception as e:
George Keishing1e7b0182021-08-06 14:05:54 -05001369 self.logger.error("\tERROR:yaml_env_vars_populate: %s" % e)
George Keishingb97a9042021-07-29 07:41:20 -05001370 pass
1371
1372 # Parse the string for plugin vars.
1373 try:
1374 # Example, list of plugin vars ['my_username', 'my_data']
1375 plugin_var_name_list = global_plugin_dict.keys()
1376 for var in plugin_var_name_list:
George Keishing9348b402021-08-13 12:22:35 -05001377 # skip env var list already populated above code block list.
George Keishing0581cb02021-08-05 15:08:58 -05001378 if var in env_var_names_list:
1379 continue
George Keishing9348b402021-08-13 12:22:35 -05001380 # If this plugin var exist but empty in dict, don't replace.
George Keishing0581cb02021-08-05 15:08:58 -05001381 # This is either a YAML plugin statement incorrectly used or
George Keishing9348b402021-08-13 12:22:35 -05001382 # user added a plugin var which is not going to be populated.
George Keishing0581cb02021-08-05 15:08:58 -05001383 if yaml_arg_str in global_plugin_dict:
1384 if isinstance(global_plugin_dict[var], (list, dict)):
1385 # List data type or dict can't be replaced, use directly
1386 # in eval function call.
1387 global_plugin_type_list.append(var)
1388 else:
Patrick Williams20f38712022-12-08 06:18:26 -06001389 yaml_arg_str = yaml_arg_str.replace(
1390 str(var), str(global_plugin_dict[var])
1391 )
George Keishing0581cb02021-08-05 15:08:58 -05001392 # Just a string like filename or command.
1393 else:
Patrick Williams20f38712022-12-08 06:18:26 -06001394 yaml_arg_str = yaml_arg_str.replace(
1395 str(var), str(global_plugin_dict[var])
1396 )
George Keishingb97a9042021-07-29 07:41:20 -05001397 except (IndexError, ValueError) as e:
George Keishing1e7b0182021-08-06 14:05:54 -05001398 self.logger.error("\tERROR: yaml_plugin_vars_populate: %s" % e)
George Keishingb97a9042021-07-29 07:41:20 -05001399 pass
1400
1401 return yaml_arg_str
George Keishing1e7b0182021-08-06 14:05:54 -05001402
1403 def plugin_error_check(self, plugin_dict):
1404 r"""
1405 Plugin error dict processing.
1406
1407 Description of argument(s):
1408 plugin_dict Dictionary of plugin error.
1409 """
Patrick Williams20f38712022-12-08 06:18:26 -06001410 if any("plugin_error" in d for d in plugin_dict):
George Keishing1e7b0182021-08-06 14:05:54 -05001411 for d in plugin_dict:
Patrick Williams20f38712022-12-08 06:18:26 -06001412 if "plugin_error" in d:
1413 value = d["plugin_error"]
George Keishing1e7b0182021-08-06 14:05:54 -05001414 # Reference if the error is set or not by plugin.
1415 return plugin_error_dict[value]
George Keishingde79a9b2021-08-12 16:14:43 -05001416
1417 def key_index_list_dict(self, key, list_dict):
1418 r"""
1419 Iterate list of dictionary and return index if the key match is found.
1420
1421 Description of argument(s):
1422 key Valid Key in a dict.
1423 list_dict list of dictionary.
1424 """
1425 for i, d in enumerate(list_dict):
1426 if key in d.keys():
1427 return i
1428
1429 def plugin_expect_type(self, type, data):
1430 r"""
1431 Plugin expect directive type check.
1432 """
Patrick Williams20f38712022-12-08 06:18:26 -06001433 if type == "int":
George Keishingde79a9b2021-08-12 16:14:43 -05001434 return isinstance(data, int)
Patrick Williams20f38712022-12-08 06:18:26 -06001435 elif type == "float":
George Keishingde79a9b2021-08-12 16:14:43 -05001436 return isinstance(data, float)
Patrick Williams20f38712022-12-08 06:18:26 -06001437 elif type == "str":
George Keishingde79a9b2021-08-12 16:14:43 -05001438 return isinstance(data, str)
Patrick Williams20f38712022-12-08 06:18:26 -06001439 elif type == "list":
George Keishingde79a9b2021-08-12 16:14:43 -05001440 return isinstance(data, list)
Patrick Williams20f38712022-12-08 06:18:26 -06001441 elif type == "dict":
George Keishingde79a9b2021-08-12 16:14:43 -05001442 return isinstance(data, dict)
Patrick Williams20f38712022-12-08 06:18:26 -06001443 elif type == "tuple":
George Keishingde79a9b2021-08-12 16:14:43 -05001444 return isinstance(data, tuple)
1445 else:
1446 self.logger.info("\tInvalid data type requested: %s" % type)
Patrick Williams20f38712022-12-08 06:18:26 -06001447 return "INVALID"