File: //lib/fm-agent/plugins/phpfpm.py
import agent_util
import logging
from agent_util import float
try:
# Python 2.x
import httplib
except:
import http.client as httplib
try:
# Python 2.x
import urlparse
except:
import urllib.parse as urlpars
logger = logging.getLogger(__name__)
def execute_query(query):
ret, output = agent_util.execute_command(query)
return str(output)
class PHPFPMPlugin(agent_util.Plugin):
textkey = "phpfpm"
label = "PHP-FPM"
@classmethod
def get_metadata(self, config):
# Installation and config checks
installed = agent_util.which("php5-fpm") or agent_util.which("php-fpm")
configured = "console_url" in config
# if config is present, trust it and proceed
if configured:
self.log.info("console_url found in config. Marking plugin as supported")
status = agent_util.SUPPORTED
msg = None
# PHP-FPM is installed, but not configured, ask the user for more config assistance
elif installed and not configured:
self.log.info("PHP FPM binary found, but console_url is not in config")
status = agent_util.MISCONFIGURED
msg = "console_url is not in config"
return {}
# PHP-FPM does not appear to be installed and no config provided, disqualify the plugin
else:
self.log.info("No console_url provided and php-fpm binary not found")
status = agent_util.UNSUPPORTED
msg = "php-fpm binary not found"
return {}
# Generate options based on the number of entries in the config file.
options = config.get("console_url").split(",")
self.log.info("PHP-FPM is supported. Generating Metadata.")
# This is the metadata for the plugin.
data = {
"active processes": {
"label": "Active processes",
"options": options,
"status": status,
"error_message": msg,
},
"idle processes": {
"label": "Idle processes",
"options": options,
"status": status,
"error_message": msg,
},
"listen queue": {
"label": "Listen queue",
"options": options,
"status": status,
"error_message": msg,
},
"listen queue len": {
"label": "Listen queue len",
"options": options,
"status": status,
"error_message": msg,
},
"max active processes": {
"label": "Max active processes",
"options": options,
"status": status,
"error_message": msg,
},
"max children reached": {
"label": "Children reached",
"options": options,
"status": status,
"error_message": msg,
},
"max listen queue": {
"label": "Max listen queue",
"options": options,
"status": status,
"error_message": msg,
},
"slow requests": {
"label": "Slow Requests",
"options": options,
"status": status,
"error_message": msg,
},
"start since": {
"label": "Start since",
"options": options,
"status": status,
"error_message": msg,
},
"start time": {
"label": "Start time",
"options": options,
"status": status,
"error_message": msg,
},
"total processes": {
"label": "Total Processes",
"options": options,
"status": status,
"error_message": msg,
},
}
return data
def check(self, textkey, data, config):
"""
Make a GET request to the console url and parse the output.
"""
if data.startswith("http"):
url = urlparse.urlparse(data + "?json&full")
if data.startswith("https:"):
connection = httplib.HTTPSConnection(host=url.netloc, timeout=25)
else:
connection = httplib.HTTPConnection(host=url.netloc, timeout=25)
connection.request("GET", "%s?%s" % (url.path, url.query))
resp = connection.getresponse()
if int(resp.status) != 200:
logging.error(
"Invalid response from %s/%s Reason: %s"
% (url.netloc, url.path, resp.reason)
)
return
else:
output = resp.read().decode("utf-8")
connection.close()
else:
query = (
r"SCRIPT_NAME=/status SCRIPT_FILENAME=/status QUERY_STRING=json\&full REQUEST_METHOD=GET cgi-fcgi -bind -connect "
+ data
+ " |tail -1"
)
ret, output = agent_util.execute_command(query)
try:
statLines = agent_util.json_loads(output)
except Exception:
logging.exception("Unable to parse json output.")
return
metric = str(textkey).replace("_", " ")
if statLines.has_key(metric):
return float(statLines[metric])
else:
raise Exception(
"stats output did not contain metric "
+ metric
+ ". stats output: "
+ statLines
)