ok
Direktori : /proc/self/root/lib/fm-agent/plugins/ |
Current File : //proc/self/root/lib/fm-agent/plugins/apache.py |
import agent_util from plugins.process import ProcessPlugin try: import ssl except: ssl = None try: # Python 2.x from httplib import HTTPConnection, HTTPSConnection except: from http.client import HTTPConnection, HTTPSConnection from library.log_matcher import LogMatcher import traceback # ON FREEBSD/CENTOS, THEY MAY NEED TO ADD THIS TO THEIR HTTPD.CONF/APACHE2.CONF: # LoadModule status_module libexec/apache22/mod_status.so # <IfModule status_module> # ExtendedStatus On # <Location /server-status> # SetHandler server-status # Order deny,allow # Allow from all # </Location> # </IfModule> class ApachePlugin(agent_util.Plugin): textkey = "apache" label = "Apache Webserver" DEFAULTS = { "server_status_protocol": "http", "server_status_host": "localhost", "server_status_url": "server-status", "apache_log_files": [ "/var/log/apache2/access.log", "/var/log/httpd/access.log", "/var/log/httpd-access.log", ], } LOG_COUNT_EXPRESSIONS = { "apache.4xx": r"4\d{2}", "apache.5xx": r"5\d{2}", "apache.2xx": r"2\d{2}", } @classmethod def get_data(self, textkey, ip, config): server_status_path = "" server_status_url = config.get("server_status_url") server_status_protocol = config.get("server_status_protocol") server_status_port = config.get("server_status_port", None) if not server_status_url.startswith("/"): server_status_path += "/" server_status_path += server_status_url + "?auto" if server_status_protocol == "https" and ssl is not None: if server_status_port is None: conn = HTTPSConnection(ip, context=ssl._create_unverified_context()) else: conn = HTTPSConnection( ip, int(server_status_port), context=ssl._create_unverified_context(), ) else: if server_status_port: conn = HTTPConnection(ip, server_status_port) else: conn = HTTPConnection(ip) try: conn.request("GET", server_status_path) r = conn.getresponse() output = r.read().decode() conn.close() except: self.log.info( """ Unable to access the Apache status page at %s%s. Please ensure Apache is running and the server status url is correctly specified. """ % (ip, server_status_path) ) self.log.info("error: %s" % traceback.format_exc()) return None data = dict() for line in output.splitlines(): if ":" not in line: continue k, v = line.split(": ", 1) data.update({k: v}) def get_param_value(data, param, output_type): try: return output_type(data[param]) except KeyError: return None if textkey.endswith("uptime"): return get_param_value(data, "Uptime", int) elif textkey.endswith("total_accesses"): return get_param_value(data, "Total Accesses", int) elif textkey.endswith("total_traffic"): val = get_param_value(data, "Total kBytes", int) # Convert to MB for backwards compatibility if val: return val / 1000.0 else: return None elif textkey.endswith("cpu_load"): return get_param_value(data, "CPULoad", float) elif textkey.endswith("connections"): return get_param_value(data, "ReqPerSec", float) elif textkey.endswith("transfer_rate"): val = get_param_value(data, "BytesPerSec", float) # Convert to MB for backwards compatibility return val / (1000.0**2) elif textkey.endswith("avg_request_size"): val = get_param_value(data, "BytesPerReq", float) # Convert to MB for backwards compatibility return val / (1000.0**2) elif textkey.endswith("workers_used_count"): return get_param_value(data, "BusyWorkers", int) elif textkey.endswith("workers_idle_count"): return get_param_value(data, "IdleWorkers", int) elif textkey in ("apache.workers_used", "apache.workers_idle"): busy = get_param_value(data, "BusyWorkers", int) idle = get_param_value(data, "IdleWorkers", int) if busy is None or idle is None: return None total = busy + idle if textkey.endswith("workers_used"): return float(100.0 * busy / total) elif textkey.endswith("workers_idle"): return float(100.0 * idle / total) @classmethod def get_metadata(self, config): status = agent_util.SUPPORTED msg = None self.log.info("Looking for apache2ctl to confirm apache is installed") # look for either overrides on how to access the apache healthpoint or one of the apachectl bins if ( not agent_util.which("apache2ctl") and not agent_util.which("apachectl") and not config.get("from_docker") and not agent_util.which("httpd") ): self.log.info("Couldn't find apachectl or apache2ctl") status = agent_util.UNSUPPORTED msg = "Apache wasn't detected (apachectl or apache2ctl)" return {} # update default config with anything provided in the config file if config: new_config = self.DEFAULTS.copy() new_config.update(config) config = new_config # Look for Apache server-status endpoint server_status_path = "" server_status_protocol = config.get( "server_status_protocol", self.DEFAULTS["server_status_protocol"] ) server_status_url = config.get( "server_status_url", self.DEFAULTS["server_status_url"] ) server_status_port = config.get("server_status_port", None) if not server_status_url.startswith("/"): server_status_path += "/" server_status_path += server_status_url + "?auto" not_found_error = ( """ Unable to access the Apache status page at %s. Please ensure the status page module is enabled, Apache is running, and, optionally, the server status url is correctly specified. See docs.fortimonitor.forticloud.com/ for more information. """ % server_status_path ) host_list = [] # support optional comma delimitted addresses ip_list = config.get("server_status_host", "localhost") ip_list = ip_list.split(",") # loop over each IP from config and check to see if the status endpoint is reachable for ip in ip_list: ip_working = True try: if server_status_protocol == "https" and ssl is not None: if server_status_port is None: conn = HTTPSConnection( ip, context=ssl._create_unverified_context() ) else: conn = HTTPSConnection( ip, int(server_status_port), context=ssl._create_unverified_context(), ) else: if server_status_port: conn = HTTPConnection(ip, server_status_port) else: conn = HTTPConnection(ip) conn.request("GET", server_status_path) r = conn.getresponse() conn.close() except Exception: import sys _, err_msg, _ = sys.exc_info() ip_working = False self.log.info(not_found_error) self.log.info("error: %s" % err_msg) msg = not_found_error continue if r.status != 200: self.log.info(not_found_error) msg = not_found_error ip_working = False if ip_working: host_list.append(ip) output = r.read() if config.get("debug", False): self.log.info( "#####################################################" ) self.log.info("Apache server-status output:") self.log.info(output) self.log.info( "#####################################################" ) if not host_list: status = agent_util.MISCONFIGURED msg = not_found_error return {} # Checking log files access if not config.get("apache_log_files"): log_files = self.DEFAULTS.get("apache_log_files") else: log_files = config.get("apache_log_files") try: if type(log_files) in (str, unicode): log_files = log_files.split(",") except NameError: if type(log_files) in (str, bytes): log_files = log_files.split(",") can_access = False log_file_msg = "" log_file_status = status for log_file in log_files: try: opened = open(log_file, "r") opened.close() # Can access at least one file. Support log access can_access = True except Exception: import sys _, error, _ = sys.exc_info() message = ( "Error opening the file %s. Ensure the fm-agent user has access to read this file" % log_file ) if "Permission denied" in str(error): self.log.error(error) self.log.error(message) if log_file not in self.DEFAULTS.get("apache_log_files", []): self.log.error(error) self.log.error(message) log_file_msg = message log_file_status = agent_util.MISCONFIGURED if can_access: log_file_status = agent_util.SUPPORTED log_file_msg = "" metadata = { "apache.workers_used": { "label": "Workers - percent serving requests", "options": host_list, "status": status, "error_message": msg, "unit": "%", }, "apache.workers_idle": { "label": "Workers - percent idle", "options": host_list, "status": status, "error_message": msg, "unit": "%", }, "apache.workers_used_count": { "label": "Workers - count serving requests", "options": host_list, "status": status, "error_message": msg, "unit": "workers", }, "apache.workers_idle_count": { "label": "Workers - count idle", "options": host_list, "status": status, "error_message": msg, "unit": "workers", }, "apache.uptime": { "label": "Server uptime", "options": host_list, "status": status, "error_message": msg, "unit": "seconds", }, "apache.total_accesses": { "label": "Request count", "options": host_list, "status": status, "error_message": msg, "unit": "requests", }, "apache.total_traffic": { "label": "Total content served", "options": host_list, "status": status, "error_message": msg, "unit": "MB", }, "apache.cpu_load": { "label": "Percentage of CPU used by all workers", "options": host_list, "status": status, "error_message": msg, "unit": "%", }, "apache.connections": { "label": "Requests per second", "options": host_list, "status": status, "error_message": msg, "unit": "requests", }, "apache.transfer_rate": { "label": "Transfer rate", "options": host_list, "status": status, "error_message": msg, "unit": "MB/s", }, "apache.avg_request_size": { "label": "Request size average", "options": host_list, "status": status, "error_message": msg, "unit": "MB", }, "apache.2xx": { "label": "Rate of 2xx's events", "options": None, "status": log_file_status, "error_message": log_file_msg, "unit": "entries/s", }, "apache.4xx": { "label": "Rate of 4xx's events", "options": None, "status": log_file_status, "error_message": log_file_msg, "unit": "entries/s", }, "apache.5xx": { "label": "Rate of 5xx's events", "options": None, "status": log_file_status, "error_message": log_file_msg, "unit": "entries/s", }, "apache.is_running": { "label": "Apache is running", "options": None, "status": status, "error_message": msg, }, } return metadata @classmethod def get_metadata_docker(self, container, config): if "server_status_host" not in config: try: ip = agent_util.get_container_ip(container) config["server_status_host"] = ip except Exception: import sys _, e, _ = sys.exc_info() self.log.exception(e) config["from_docker"] = True return self.get_metadata(config) def get_apache_process_name(self): if agent_util.which("apache2ctl"): return "apache2" if agent_util.which("httpd"): return "httpd" if agent_util.which("httpd22"): return "httpd22" return None def check(self, textkey, ip, config): # update default config with anything provided in the config file new_config = self.DEFAULTS.copy() new_config.update(config) config = new_config # add backwards compatibility for older entries where IP was not an option. Pull from config instead. if not ip: ip = config["server_status_host"].split(",")[0] if textkey == "apache.is_running" and not config.get("from_docker"): apache_process = ProcessPlugin(None) apache_process_name = self.get_apache_process_name() if apache_process_name is not None: apache_is_running = apache_process.check( "process.exists", apache_process_name, {} ) return apache_is_running return None if textkey in ("apache.2xx", "apache.4xx", "apache.5xx"): file_inodes = {} total_metrics = 0 timescale = 1 column = 8 expression = self.LOG_COUNT_EXPRESSIONS.get(textkey) if not config.get("apache_log_files"): log_files = config["apache_log_files"] else: log_files = config.get("apache_log_files") try: if type(log_files) in (str, unicode): log_files = log_files.split(",") except NameError: if type(log_files) in (str, bytes): log_files = log_files.split(",") for target in log_files: try: file_inodes[target] = LogMatcher.get_file_inode(target) except OSError: import sys _, error, _ = sys.exc_info() if "Permission denied" in str(error): self.log.error( "Error opening the file %s. Ensure the fm-agent user has access to read this file" % target ) self.log.error(str(error)) if target not in self.DEFAULTS.get("apache_log_files", []): self.log.error(str(error)) self.log.error( "Error opening the file %s. Ensure the fm-agent user has access to read this file" % target ) continue log_data = self.get_cache_results( textkey, "%s/%s" % (self.schedule.id, target) ) if log_data: log_data = log_data[0][-1] else: log_data = dict() last_line_number = log_data.get("last_known_line") stored_inode = log_data.get("inode") results = log_data.get("results", []) try: total_lines, current_lines = LogMatcher.get_file_lines( last_line_number, target, file_inodes[target], stored_inode ) except IOError: import sys _, error, _ = sys.exc_info() self.log.error( "Unable to read the file %s. Ensure the fm-agent user has access to read this file" % target ) continue self.log.info( "Stored line %s Current line %s looking at %s lines" % (str(last_line_number), str(total_lines), str(len(current_lines))) ) log_matcher = LogMatcher(stored_inode) results = log_matcher.match_in_column(current_lines, expression, column) metric, results = log_matcher.calculate_metric(results, timescale) total_metrics += metric and metric or 0 self.log.info( 'Found %s instances of "%s" in %s' % (str(metric or 0), expression, target) ) previous_result = self.get_cache_results( textkey, "%s/%s" % (self.schedule.id, target) ) cache_data = dict( inode=file_inodes[target], last_known_line=total_lines, results=results, ) self.cache_result( textkey, "%s/%s" % (self.schedule.id, target), cache_data, replace=True, ) if not previous_result: return None else: delta, prev_data = previous_result[0] try: curr_count = cache_data.get("results")[0][-1] result = curr_count / float(delta) except IndexError: result = None return result else: return ApachePlugin.get_data(textkey, ip, config) def check_docker(self, container, textkey, ip, config): try: ip = agent_util.get_container_ip(container) except: return None return self.check(textkey, ip, config)