165 lines
5.3 KiB
Python
165 lines
5.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
import gevent
|
|
|
|
from amplify.agent.common.context import context
|
|
from amplify.agent.common.util.timeout import TimeoutException
|
|
|
|
from amplify.ext.phpfpm.util.inet import INET_IPV4
|
|
from amplify.ext.phpfpm.util.fcgi import FCGIApp
|
|
|
|
|
|
__author__ = "Grant Hulegaard"
|
|
__copyright__ = "Copyright (C) Nginx, Inc. All rights reserved."
|
|
__license__ = ""
|
|
__maintainer__ = "Grant Hulegaard"
|
|
__email__ = "grant.hulegaard@nginx.com"
|
|
|
|
|
|
class PHPFPMStatus(object):
|
|
"""
|
|
Query wrapper around FCGIApp. Responsible for properly initializing and
|
|
calling FCGIApp with exception handling.
|
|
"""
|
|
def __init__(self, path=None, host=None, port=None, url=None):
|
|
self._path = path
|
|
self._host = host
|
|
self._port = port
|
|
|
|
self.connection = None
|
|
self._setup_connection()
|
|
|
|
self.env = {}
|
|
self._setup_env(url)
|
|
|
|
def _setup_connection(self):
|
|
"""
|
|
Setup connection information. IPV4 or Unix file socket.
|
|
|
|
Follows a similar pattern from flup.
|
|
"""
|
|
if self._host:
|
|
assert self._port
|
|
self.connection = INET_IPV4(self._host, self._port)
|
|
|
|
if self.connection is None:
|
|
assert self._path
|
|
self.connection = self._path
|
|
|
|
def _setup_env(self, url):
|
|
"""
|
|
Setup environment variables to pass though CGI
|
|
"""
|
|
self.env = {
|
|
'SCRIPT_FILENAME': url,
|
|
'QUERY_STRING': '',
|
|
'REQUEST_METHOD': 'GET',
|
|
'SCRIPT_NAME': url,
|
|
'REQUEST_URI': url,
|
|
'GATEWAY_INTERFACE': 'CGI/1.1',
|
|
'SERVER_SOFTWARE': 'amplify-agent',
|
|
'REDIRECT_STATUS': '200',
|
|
'CONTENT_TYPE': '',
|
|
'CONTENT_LENGTH': '0',
|
|
# 'DOCUMENT_URI': url,
|
|
'DOCUMENT_ROOT': '/var/www/',
|
|
'SERVER_PROTOCOL': 'HTTP/1.1',
|
|
'REMOTE_ADDR': '127.0.0.1',
|
|
'REMOTE_PORT': '123'
|
|
}
|
|
|
|
if isinstance(self.connection, INET_IPV4):
|
|
self.env.update({
|
|
'SERVER_ADDR': self.connection.host,
|
|
'SERVER_PORT': str(self.connection.port),
|
|
'SERVER_NAME': self.connection.host
|
|
})
|
|
elif isinstance(self.connection, str):
|
|
self.env.update({
|
|
'SERVER_ADDR': self.connection,
|
|
'SERVER_NAME': self.connection
|
|
})
|
|
|
|
def _connect(self):
|
|
"""
|
|
Initialize an FCGIApp wrapper from flup. Since FCGIApp doesn't open a
|
|
socket until call, we don't need try-except handling.
|
|
"""
|
|
# TODO: Should we cache this FCGIApp object? Only init once instead of
|
|
# per call?
|
|
if self.connection is not None:
|
|
# if _connect is a string, assume it is a string path for a Unix
|
|
# File sock
|
|
if isinstance(self.connection, str):
|
|
fcgi = FCGIApp(
|
|
connect=self.connection
|
|
)
|
|
elif isinstance(self.connection, INET_IPV4):
|
|
fcgi = FCGIApp(
|
|
host=self.connection.host, port=self.connection.port
|
|
)
|
|
else:
|
|
fcgi = FCGIApp(
|
|
connect=self.connection
|
|
)
|
|
# this is a hail mary that will bubble a NotImplemented error
|
|
# from FCGIApp if flup can't handle it.
|
|
|
|
return fcgi
|
|
|
|
def get_status(self):
|
|
"""
|
|
Now with meta information all setup, attempt to communicate over socket
|
|
and get status page.
|
|
|
|
Example return::
|
|
pool: www
|
|
process manager: dynamic
|
|
start time: 07/Dec/2016:00:13:21 +0000
|
|
start since: 0
|
|
accepted conn: 1
|
|
listen queue: 0
|
|
max listen queue: 0
|
|
listen queue len: 0
|
|
idle processes: 0
|
|
active processes: 1
|
|
total processes: 1
|
|
max active processes: 1
|
|
max children reached: 0
|
|
slow requests: 0
|
|
"""
|
|
try:
|
|
with gevent.Timeout(10, TimeoutException):
|
|
fcgi = self._connect()
|
|
resp = fcgi(self.env, lambda x, y: None)
|
|
except TimeoutException:
|
|
context.log.error(
|
|
'pool communication at "%s" timed out' %
|
|
self.connection.__str__()
|
|
) # use .__str__() because of namedtuple
|
|
context.log.debug('additional info:', exc_info=True)
|
|
resp = ('500', [], '', '')
|
|
except:
|
|
context.log.error(
|
|
'failed to communicate with pool at "%s"' %
|
|
self.connection.__str__()
|
|
) # use .__str__() because of namedtuple
|
|
context.log.debug('additional info:', exc_info=True)
|
|
resp = ('500', [], '', '')
|
|
|
|
status, headers, out, err = resp
|
|
|
|
if status.startswith('200'):
|
|
return out
|
|
else:
|
|
context.log.debug(
|
|
'non-success returned by fcgi (status: %s)' % status
|
|
)
|
|
context.log.debug(
|
|
'additional info:\n'
|
|
' status: %s\n'
|
|
' headers: %s\n'
|
|
' out: %s\n'
|
|
' err: %s\n'
|
|
% resp
|
|
)
|