#!/usr/bin/env python # coding=utf-8 __author__ = "TrackMe Limited" __copyright__ = "Copyright 2022-2026, TrackMe Limited, U.K." __credits__ = "TrackMe Limited, U.K." __license__ = "TrackMe Limited, all rights reserved" __version__ = "0.1.0" __maintainer__ = "TrackMe Limited, U.K." __email__ = "support@trackme-solutions.com" __status__ = "PRODUCTION" # Standard library imports import json import logging import os import re import sys import time # Third-party library imports import requests import urllib3 import urllib.parse from logging.handlers import RotatingFileHandler # Disable insecure request warnings for urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # set splunkhome splunkhome = os.environ["SPLUNK_HOME"] # set logging filehandler = RotatingFileHandler( "%s/var/log/splunk/trackme_api_autodocs.log" % splunkhome, mode="a", maxBytes=10000000, backupCount=1, ) formatter = logging.Formatter( "%(asctime)s %(levelname)s %(filename)s %(funcName)s %(lineno)d %(message)s" ) logging.Formatter.converter = time.gmtime filehandler.setFormatter(formatter) log = logging.getLogger() # root logger - Good to get it only once. for hdlr in log.handlers[:]: # remove the existing file handlers if isinstance(hdlr, logging.FileHandler): log.removeHandler(hdlr) log.addHandler(filehandler) # set the new handler # set the log level to INFO, DEBUG as the default is ERROR log.setLevel(logging.INFO) # append current directory sys.path.append(os.path.dirname(os.path.abspath(__file__))) # import libs import import_declare_test # Import Splunk libs from splunklib.searchcommands import ( dispatch, GeneratingCommand, Configuration, Option, validators, ) # Import trackme libs from trackme_libs import trackme_reqinfo # list of TrackMe API handlers from trackme_rest_handler_alerting_user import ( TrackMeHandlerAlertingReadOps_v2 as handler_alerting_user, ) from trackme_rest_handler_alerting_admin import ( TrackMeHandlerAlertingWriteOps_v2 as handler_alerting_admin, ) from trackme_rest_handler_ack_user import TrackMeHandlerAckReadOps_v2 as handler_ack_user from trackme_rest_handler_ack_power import TrackMeHandlerAckWriteOps_v2 as handler_ack_power from trackme_rest_handler_audit import TrackMeHandlerAudit_v2 as handler_audit from trackme_rest_handler_backup_and_restore import ( TrackMeHandlerBackupAndRestore_v2 as handler_backup_and_restore, ) from trackme_rest_handler_configuration import ( TrackMeHandlerConfigurationRead_v2 as handler_configuration, ) from trackme_rest_handler_configuration_admin import ( TrackMeHandlerConfigurationAdmin_v2 as handler_configuration_admin, ) from trackme_rest_handler_maintenance import ( TrackMeHandlerMaintenance_v2 as handler_maintenance, ) from trackme_rest_handler_maintenance_kdb_user import ( TrackMeHandlerMaintenanceKdbRead_v2 as handler_maintenance_kdb_user, ) from trackme_rest_handler_maintenance_kdb_admin import ( TrackMeHandlerMaintenanceKdbAdmin_v2 as handler_maintenance_kdb_admin, ) from trackme_rest_handler_bank_holidays_user import ( TrackMeHandlerBankHolidaysRead_v2 as handler_bank_holidays_user, ) from trackme_rest_handler_bank_holidays_admin import ( TrackMeHandlerBankHolidaysAdmin_v2 as handler_bank_holidays_admin, ) from trackme_rest_handler_splk_flx_user import ( TrackMeHandlerSplkFlxTrackingRead_v2 as handler_splk_flx_user, ) from trackme_rest_handler_splk_flx_power import ( TrackMeHandlerSplkFlxTrackingWrite_v2 as handler_splk_flx_power, ) from trackme_rest_handler_splk_flx_admin import ( TrackMeHandlerSplkFlxTrackingAdmin_v2 as handler_splk_flx_admin, ) from trackme_rest_handler_splk_fqm_user import ( TrackMeHandlerSplkFqmTrackingRead_v2 as handler_splk_fqm_user, ) from trackme_rest_handler_splk_fqm_power import ( TrackMeHandlerSplkFqmTrackingWrite_v2 as handler_splk_fqm_power, ) from trackme_rest_handler_splk_fqm_admin import ( TrackMeHandlerSplkFqmTrackingAdmin_v2 as handler_splk_fqm_admin, ) from trackme_rest_handler_splk_wlk_user import ( TrackMeHandlerSplkWlkRead_v2 as handler_splk_wlk_user, ) from trackme_rest_handler_splk_wlk_power import ( TrackMeHandlerSplkWlkWrite_v2 as handler_splk_wlk_power, ) from trackme_rest_handler_splk_wlk_admin import ( TrackMeHandlerSplkWlkAdmin_v2 as handler_splk_wlk_admin, ) from trackme_rest_handler_splk_data_sampling_user import ( TrackMeHandlerSplkDataSamplingRead_v2 as handler_spk_data_sampling_user, ) from trackme_rest_handler_splk_data_sampling_power import ( TrackMeHandlerSplkDataSamplingWrite_v2 as handler_spk_data_sampling_power, ) from trackme_rest_handler_splk_blocklist_user import ( TrackMeHandlerSplkBlocklistRead_v2 as handler_splk_blocklist_user, ) from trackme_rest_handler_splk_blocklist_power import ( TrackMeHandlerSplkBlocklistWrite_v2 as handler_splk_blocklist_power, ) from trackme_rest_handler_splk_dhm_user import ( TrackMeHandlerSplkDhmRead_v2 as handler_splk_dhm_user, ) from trackme_rest_handler_splk_dhm_power import ( TrackMeHandlerSplkDhmWrite_v2 as handler_splk_dhm_power, ) from trackme_rest_handler_splk_dsm_user import ( TrackMeHandlerSplkDsmRead_v2 as handler_splk_dsm_user, ) from trackme_rest_handler_splk_dsm_power import ( TrackMeHandlerSplkDsmWrite_v2 as handler_splk_dsm_power, ) from trackme_rest_handler_splk_disruption_user import ( TrackMeHandlerSplkDisruptionRead_v2 as handler_splk_disruption_user, ) from trackme_rest_handler_splk_disruption_power import ( TrackMeHandlerSplkDisruptionWrite_v2 as handler_splk_disruption_power, ) from trackme_rest_handler_splk_elastic_sources_user import ( TrackMeHandlerSplkElasticSourcesRead_v2 as handler_splk_elastic_sources_user, ) from trackme_rest_handler_splk_elastic_sources_admin import ( TrackMeHandlerSplkElasticSourcesAdmin_v2 as handler_splk_elastic_sources_admin, ) from trackme_rest_handler_splk_hybrid_trackers_user import ( TrackMeHandlerSplkHybridTrackerRead_v2 as handler_splk_hybrid_trackers_user, ) from trackme_rest_handler_splk_hybrid_trackers_admin import ( TrackMeHandlerSplkHybridTrackerAdmin_v2 as handler_splk_hybrid_trackers_admin, ) from trackme_rest_handler_splk_replica_trackers_user import ( TrackMeHandlerSplkReplicaTrackerRead_v2 as handler_splk_replica_trackers_user, ) from trackme_rest_handler_splk_replica_trackers_admin import ( TrackMeHandlerSplkReplicaTrackerAdmin_v2 as handler_splk_replica_trackers_admin, ) from trackme_rest_handler_splk_identity_cards_user import ( TrackMeHandlerSplkIdentityCardsRead_v2 as handler_splk_identity_cards_user, ) from trackme_rest_handler_splk_identity_cards_power import ( TrackMeHandlerSplkIdentityCardsWrite_v2 as handler_splk_identity_cards_power, ) from trackme_rest_handler_splk_lagging_classes_user import ( TrackMeHandlerSplkLaggingClassesRead_v2 as handler_splk_lagging_classes_user, ) from trackme_rest_handler_splk_lagging_classes_power import ( TrackMeHandlerSplkLaggingClassesWrite_v2 as handler_splk_lagging_classes_power, ) from trackme_rest_handler_splk_logical_groups_user import ( TrackMeHandlerSplkLogicalGroupsRead_v2 as handler_splk_logical_groups_user, ) from trackme_rest_handler_splk_logical_groups_power import ( TrackMeHandlerSplkLogicalGroupsWrite_v2 as handler_splk_logical_groups_power, ) from trackme_rest_handler_splk_mhm_user import ( TrackMeHandlerSplkMhmRead_v2 as handler_splk_mhm_user, ) from trackme_rest_handler_splk_mhm_power import ( TrackMeHandlerSplkMhmWrite_v2 as handler_splk_mhm_power, ) from trackme_rest_handler_splk_outliers_engine_user import ( TrackMeHandlerSplkOutliersEngineRead_v2 as handler_splk_outliers_engine_user, ) from trackme_rest_handler_splk_outliers_engine_power import ( TrackMeHandlerSplkOutliersEngineWrite_v2 as handler_splk_outliers_engine_power, ) from trackme_rest_handler_splk_smart_status import ( TrackMeHandlerSplkSmartStatus_v2 as handler_splk_smart_status, ) from trackme_rest_handler_splk_tag_policies_user import ( TrackMeHandlerSplkTagPoliciesRead_v2 as handler_splk_lag_policies_user, ) from trackme_rest_handler_splk_tag_policies_power import ( TrackMeHandlerSplkTagPoliciesWrite_v2 as handler_splk_lag_policies_power, ) from trackme_rest_handler_splk_priority_policies_user import ( TrackMeHandlerSplkPriorityPoliciesRead_v2 as handler_splk_priority_policies_user, ) from trackme_rest_handler_splk_priority_policies_power import ( TrackMeHandlerSplkPriorityPoliciesWrite_v2 as handler_splk_priority_policies_power, ) from trackme_rest_handler_splk_sla_policies_user import ( TrackMeHandlerSplkSlaPoliciesRead_v2 as handler_splk_sla_policies_user, ) from trackme_rest_handler_splk_sla_policies_power import ( TrackMeHandlerSplkSlaPoliciesWrite_v2 as handler_splk_sla_policies_power, ) from trackme_rest_handler_vtenants_user import ( TrackMeHandlerVtenantsRead_v2 as handler_vtenants_user, ) from trackme_rest_handler_vtenants_power import ( TrackMeHandlerVtenantsWrite_v2 as handler_vtenants_power, ) from trackme_rest_handler_vtenants_admin import ( TrackMeHandlerVtenantsAdmin_v2 as handler_vtenants_admin, ) from trackme_rest_handler_licensing_admin import ( TrackMeHandlerLicensingAdmin_v2 as handler_licensing_admin, ) from trackme_rest_handler_licensing_user import ( TrackMeHandlerLicensingRead_v2 as handler_licensing_user, ) from trackme_rest_handler_splk_deleted_entities_user import ( TrackMeHandlerSplkDeletedEntitiesRead_v2 as handler_splk_deleted_entities_user, ) from trackme_rest_handler_splk_deleted_entities_power import ( TrackMeHandlerSplkDeletedEntitiesPower_v2 as handler_splk_deleted_entities_power, ) # Try importing the handlers that might fail try: from trackme_rest_handler_splk_soar_user import ( TrackMeHandlerSplkSoarRead_v2 as handler_splk_soar_user, ) from trackme_rest_handler_splk_soar_admin import ( TrackMeHandlerSplkSoarAdmin_v2 as handler_splk_soar_admin, ) except ImportError: # Handle the import error handler_splk_soar_user = None handler_splk_soar_admin = None logging.warning( "Could not import TrackMeHandlerSplkSoar_v2 handlers. Some features may not be available." ) from trackme_rest_handler_component_user import ( TrackMeHandlerComponentRead_v2 as handler_component_user, ) from trackme_rest_handler_component_power import ( TrackMeHandlerComponentPower_v2 as handler_component_power, ) from trackme_rest_handler_notes_user import ( TrackMeHandlerNotesRead_v2 as handler_notes_user, ) from trackme_rest_handler_notes_power import ( TrackMeHandlerNotesWrite_v2 as handler_notes_power, ) @Configuration(distributed=False) class TrackMeApiAutoDocs(GeneratingCommand): target = Option( doc=""" **Syntax:** **target=**** **Description:** The type of objects to be returned, valid options are: groups | endpoints""", require=False, default="endpoints", validate=validators.Match("mode", r"^(?:groups|endpoints)$"), ) def spl_to_curl(self, spl_example): # Return early if the example is not available if spl_example == "Not available": return spl_example # Initialize placeholders url = None mode = "GET" # Default method body = None # Split by space to isolate key=value pairs parts = spl_example.split() # Flags to denote if we are within the body or params argument in_body = False in_params = False body_parts = [] params_parts = [] for part in parts: if "url=" in part: url = part.split("url=")[1].strip('"') elif "mode=" in part: mode = part.split("mode=")[1].strip( '"' ) # Strip any existing quotes around mode elif "body=" in part: in_body = True body_parts.append(part.split("body=", 1)[1]) elif "params=" in part: in_params = True params_parts.append(part.split("params=", 1)[1]) elif in_body: body_parts.append(part) elif in_params: params_parts.append(part) # Process body if present if body_parts: body_str = " ".join(body_parts).strip('"') body = body_str.replace("'", '"') body = body.replace('"', '\\"') # Process params if present and encode them if params_parts: params_str = " ".join(params_parts).strip('"') # Assuming the params are in a JSON-like dictionary format, convert to actual JSON try: params_dict = json.loads(params_str.replace("'", '"')) except json.JSONDecodeError: params_dict = {} # Encode parameters encoded_params = "&".join( [ f"{key}={urllib.parse.quote_plus(str(value))}" for key, value in params_dict.items() ] ) # Convert the parsed info into a curl command curl_example = f"curl -u username https://mysplunk:8089{url}" if mode.upper() != "GET": curl_example += f' -X "{mode.upper()}"' if body: curl_example += f' -d "{body}"' if params_parts and mode.upper() == "GET": # Append encoded parameters directly to URL in curl command curl_example += f"?{encoded_params}" return curl_example def generate(self, **kwargs): # Get request info and set logging level reqinfo = trackme_reqinfo( self._metadata.searchinfo.session_key, self._metadata.searchinfo.splunkd_uri ) log.setLevel(reqinfo["logging_level"]) # API catalog # for each rest handler, set the associated resource group handlers_api_catalog = { handler_alerting_user: { "resource_group": "alerting", }, handler_alerting_admin: { "resource_group": "alerting/admin", }, handler_ack_user: { "resource_group": "ack", }, handler_ack_power: { "resource_group": "ack/write", }, handler_audit: { "resource_group": "audit", }, handler_backup_and_restore: { "resource_group": "backup_and_restore", }, handler_configuration: { "resource_group": "configuration", }, handler_configuration_admin: { "resource_group": "configuration/admin", }, handler_maintenance: { "resource_group": "maintenance", }, handler_maintenance_kdb_user: { "resource_group": "maintenance_kdb", }, handler_maintenance_kdb_admin: { "resource_group": "maintenance_kdb/admin", }, handler_bank_holidays_user: { "resource_group": "bank_holidays", }, handler_bank_holidays_admin: { "resource_group": "bank_holidays/admin", }, handler_splk_flx_user: { "resource_group": "splk_flx", }, handler_splk_flx_power: { "resource_group": "splk_flx/write", }, handler_splk_flx_admin: { "resource_group": "splk_flx/admin", }, handler_splk_fqm_user: { "resource_group": "splk_fqm", }, handler_splk_fqm_power: { "resource_group": "splk_fqm/write", }, handler_splk_fqm_admin: { "resource_group": "splk_fqm/admin", }, handler_spk_data_sampling_user: { "resource_group": "splk_data_sampling", }, handler_spk_data_sampling_power: { "resource_group": "splk_data_sampling/write", }, handler_splk_blocklist_user: { "resource_group": "splk_blocklist", }, handler_splk_blocklist_power: { "resource_group": "splk_blocklist/write", }, handler_splk_dhm_user: { "resource_group": "splk_dhm", }, handler_splk_dhm_power: { "resource_group": "splk_dhm/write", }, handler_splk_dsm_user: { "resource_group": "splk_dsm", }, handler_splk_dsm_power: { "resource_group": "splk_dsm/write", }, handler_splk_disruption_user: { "resource_group": "splk_disruption", }, handler_splk_disruption_power: { "resource_group": "splk_disruption/write", }, handler_splk_flx_user: { "resource_group": "splk_flx", }, handler_splk_flx_power: { "resource_group": "splk_flx/write", }, handler_splk_flx_admin: { "resource_group": "splk_flx/admin", }, handler_splk_fqm_user: { "resource_group": "splk_fqm", }, handler_splk_fqm_power: { "resource_group": "splk_fqm/write", }, handler_splk_fqm_admin: { "resource_group": "splk_fqm/admin", }, handler_splk_elastic_sources_user: { "resource_group": "splk_elastic_sources", }, handler_splk_elastic_sources_admin: { "resource_group": "splk_elastic_sources/admin", }, handler_splk_hybrid_trackers_user: { "resource_group": "splk_hybrid_trackers", }, handler_splk_hybrid_trackers_admin: { "resource_group": "splk_hybrid_trackers/admin", }, handler_splk_replica_trackers_user: { "resource_group": "splk_replica_trackers", }, handler_splk_replica_trackers_admin: { "resource_group": "splk_replica_trackers/admin", }, handler_splk_identity_cards_user: { "resource_group": "splk_identity_cards", }, handler_splk_identity_cards_power: { "resource_group": "splk_identity_cards/write", }, handler_splk_lagging_classes_user: { "resource_group": "splk_lagging_classes", }, handler_splk_lagging_classes_power: { "resource_group": "splk_lagging_classes/write", }, handler_splk_logical_groups_user: { "resource_group": "splk_logical_groups", }, handler_splk_logical_groups_power: { "resource_group": "splk_logical_groups/write", }, handler_splk_outliers_engine_user: { "resource_group": "splk_outliers_engine", }, handler_splk_outliers_engine_power: { "resource_group": "splk_outliers_engine/write", }, handler_splk_mhm_user: { "resource_group": "splk_mhm", }, handler_splk_mhm_power: { "resource_group": "splk_mhm/write", }, handler_splk_wlk_user: { "resource_group": "splk_wlk", }, handler_splk_wlk_power: { "resource_group": "splk_wlk/write", }, handler_splk_wlk_admin: { "resource_group": "splk_wlk/admin", }, handler_splk_smart_status: { "resource_group": "splk_smart_status", }, handler_splk_lag_policies_user: { "resource_group": "splk_tag_policies", }, handler_splk_lag_policies_power: { "resource_group": "splk_tag_policies/write", }, handler_splk_priority_policies_user: { "resource_group": "splk_priority_policies", }, handler_splk_priority_policies_power: { "resource_group": "splk_priority_policies/write", }, handler_splk_sla_policies_user: { "resource_group": "splk_sla_policies", }, handler_splk_sla_policies_power: { "resource_group": "splk_sla_policies/write", }, handler_vtenants_user: { "resource_group": "vtenants", }, handler_vtenants_power: { "resource_group": "vtenants/write", }, handler_vtenants_admin: { "resource_group": "vtenants/admin", }, handler_licensing_user: { "resource_group": "licensing", }, handler_licensing_admin: { "resource_group": "licensing/admin", }, handler_splk_soar_user: { "resource_group": "splk_soar", }, handler_splk_soar_admin: { "resource_group": "splk_soar/admin", }, handler_splk_deleted_entities_user: { "resource_group": "splk_deleted_entities", }, handler_splk_deleted_entities_power: { "resource_group": "splk_deleted_entities/write", }, handler_component_user: { "resource_group": "component", }, handler_component_power: { "resource_group": "component/write", }, handler_notes_user: { "resource_group": "notes", }, handler_notes_power: { "resource_group": "notes/write", }, } # # functions # def getFunctions(handlerName): # These functions should be ignored excluded_functions = ["get_forms_args_as_dict", "get_function_signature"] # loop through the handler and list the functions handler_functions = [] for item in dir(handlerName): if ( item.startswith("post_") or item.startswith("get_") or item.startswith("delete_") ): if item not in excluded_functions: # act depending on the target if self.target == "groups": if item.startswith("get_resource_group_desc"): handler_functions.append(item) elif self.target == "endpoints": if not item.startswith("get_resource_group_desc"): handler_functions.append(item) return handler_functions def yieldResponse(functionName, resource_group): try: # extract the HTTP mode from the function name using rex result = re.search(r"^(?:(post|get|delete))_(.*)", trackme_function) if result: resource_mode = result.group(1) resource_target = result.group(2) resource_api = ( f"services/trackme/v2/{resource_group}/{resource_target}" ) target_url = ( f"{self._metadata.searchinfo.splunkd_uri}/{resource_api}" ) request_data = {"describe": "true"} if resource_mode == "get": response = requests.get( target_url, headers=headers, data=json.dumps(request_data, indent=0), verify=False, timeout=600, ) elif resource_mode == "post": response = requests.post( target_url, headers=headers, data=json.dumps(request_data, indent=0), verify=False, timeout=600, ) elif resource_mode == "delete": response = requests.delete( target_url, headers=headers, data=json.dumps(request_data, indent=0), verify=False, timeout=600, ) response_json = json.loads(response.text) # get values from API response if self.target == "groups": try: resource_group_name = response_json.get( "resource_group_name" ) except Exception as e: resource_group_name = None try: resource_group_desc = response_json.get( "resource_group_desc" ) except Exception as e: resource_group_desc = None response = { "resource_group": resource_group_name, "resource_desc": resource_group_desc, "python_function": trackme_function, } elif self.target == "endpoints": try: resource_desc = response_json.get("resource_desc") except Exception as e: resource_desc = None try: resource_spl_example = response_json.get( "resource_spl_example" ) except Exception as e: resource_spl_example = None # get the resource curl example resource_curl_example = ( self.spl_to_curl(resource_spl_example) if resource_spl_example else None ) response = { "resource_group": resource_group, "resource_desc": resource_desc, "resource_api": resource_api, "resource_describe": response_json, "resource_mode": resource_mode, "resource_spl_example": resource_spl_example, "resource_curl_example": resource_curl_example, "python_function": trackme_function, } return response else: raise Exception( f'failed to extract the HTTP mode for function="{functionName}" in resource_group="{resource_group}"' ) except Exception as e: raise Exception( f'failed to retrieve a valid response for function="{functionName}" in resource_group="{resource_group}" with exception="{str(e)}", response="{response.text}", target_url="{target_url}"' ) # # start # start = time.time() # Get the session key session_key = self._metadata.searchinfo.session_key # build header and target headers = { "Authorization": "Splunk " + str(session_key), "Content-Type": "application/json", } # Loop through the API handlers for handler_api in handlers_api_catalog: # get the resource group from the catalog resource_group = handlers_api_catalog[handler_api].get("resource_group") # loop through the handler and list the functions handler_functions = getFunctions(handler_api) # loop through the functions in this handler, get and yield the results for trackme_function in handler_functions: try: response = yieldResponse(trackme_function, resource_group) # yield if self.target == "groups": yield { "_time": time.time(), "_raw": { "resource_group": resource_group, "resource_desc": response.get("resource_desc"), "python_function": response.get("python_function"), }, "resource_group": resource_group, "resource_desc": response.get("resource_desc"), "python_function": response.get("python_function"), } elif self.target == "endpoints": resource_describe = response.get("resource_describe") # remove redondant resource_spl_example and resource_desc if resource_describe: resource_describe.pop("resource_spl_example", None) resource_describe.pop("resource_curl_example", None) resource_describe.pop("resource_desc", None) yield { "_time": time.time(), "_raw": { "resource_group": resource_group, "resource_desc": response.get("resource_desc"), "resource_api": response.get("resource_api"), "resource_describe": resource_describe, "resource_mode": response.get("resource_mode"), "resource_spl_example": response.get( "resource_spl_example" ), "resource_curl_example": response.get( "resource_curl_example" ), "python_function": response.get("python_function"), }, "resource_group": resource_group, "resource_desc": response.get("resource_desc"), "resource_api": response.get("resource_api"), "resource_describe": resource_describe, "resource_mode": response.get("resource_mode"), "resource_spl_example": response.get( "resource_spl_example" ), "resource_curl_example": response.get( "resource_curl_example" ), "python_function": response.get("python_function"), } except Exception as e: logging.error( f'failed to generate API doc result from exception="{str(e)}"' ) yield { "_time": time.time(), "_raw": f'failed to generate API doc result for function="{trackme_function}" with exception="{str(e)}"', } run_time = round(time.time() - start) logging.info(f"trackmeapiautodocs execution terminated, run_time={run_time}") dispatch(TrackMeApiAutoDocs, sys.argv, sys.stdin, sys.stdout, __name__)