Provides emergency WSGI application capable of saving reports to disk to work as a backup in case of server issues.
Closes: #162 #175
Signed-off-by: Richard Marko rmarko@redhat.com --- configure.ac | 1 + faf.spec.in | 23 ++++++++- pyfaf/Makefile.am | 2 +- pyfaf/emergency/Makefile.am | 10 ++++ pyfaf/emergency/__init__.py | 0 pyfaf/emergency/emergency.wsgi | 88 +++++++++++++++++++++++++++++++++++ pyfaf/emergency/faf-emergency.conf.in | 16 +++++++ 7 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 pyfaf/emergency/Makefile.am create mode 100644 pyfaf/emergency/__init__.py create mode 100644 pyfaf/emergency/emergency.wsgi create mode 100644 pyfaf/emergency/faf-emergency.conf.in
diff --git a/configure.ac b/configure.ac index b0117a9..4e39ba1 100644 --- a/configure.ac +++ b/configure.ac @@ -52,6 +52,7 @@ AC_CONFIG_FILES([ tests/utils/Makefile pyfaf/client/Makefile pyfaf/client/commands/Makefile + pyfaf/emergency/Makefile pyfaf/hub/Makefile pyfaf/hub/common/Makefile pyfaf/hub/problems/Makefile diff --git a/faf.spec.in b/faf.spec.in index 6effe3e..5ffa84c 100644 --- a/faf.spec.in +++ b/faf.spec.in @@ -93,6 +93,20 @@ Requires(postun): systemd-units httpd %description hub Kobo hub for %{name} tasks
+%package emergency +Summary: Emergency WSGI application +Group: System Environment/Libraries +Requires: %{name} = %{version} +Requires: httpd +Requires: mod_wsgi +BuildArch: noarch + +%description emergency +Provides emergency WSGI application +capable of saving reports to disk +to work as a backup in case of server +issues. + %package worker Summary: Kobo worker for %{name} tasks Group: System Environment/Libraries @@ -145,9 +159,10 @@ mkdir -p %{buildroot}%{_localstatedir}/log/faf/build mkdir -p %{buildroot}%{_datadir}/faf/hub/static # On Fedora, the Python WSGI module is loaded by wsgi.conf in the # conf.d HTTPD configuration directory. faf-hub.conf requires WSGI to -# be already active. Here we rename the faf-hub.conf to be loaded -# after wsgi.conf and not before. +# be already active. Here we rename the faf-hub.conf and +# faf-emergency.conf to be loaded after wsgi.conf and not before. mv %{buildroot}%{_sysconfdir}/httpd/conf.d/{,wsgi-}faf-hub.conf +mv %{buildroot}%{_sysconfdir}/httpd/conf.d/{,wsgi-}faf-emergency.conf
%check @@ -297,6 +312,10 @@ fi %dir %attr(0770, faf, faf) %{_localstatedir}/spool/faf/hub/upload %{_datadir}/faf/hub
+%files emergency +%config(noreplace) %{_sysconfdir}/httpd/conf.d/wsgi-faf-emergency.conf +%{python_sitelib}/pyfaf/emergency + %files worker %config(noreplace) %{_sysconfdir}/faf/worker.conf %{python_sitelib}/pyfaf/worker diff --git a/pyfaf/Makefile.am b/pyfaf/Makefile.am index 533ccd4..c97b1a8 100644 --- a/pyfaf/Makefile.am +++ b/pyfaf/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = client hub worker storage +SUBDIRS = client emergency hub worker storage
pyfaf_PYTHON = \ __init__.py \ diff --git a/pyfaf/emergency/Makefile.am b/pyfaf/emergency/Makefile.am new file mode 100644 index 0000000..26bfc90 --- /dev/null +++ b/pyfaf/emergency/Makefile.am @@ -0,0 +1,10 @@ +emergency_PYTHON = emergency.wsgi __init__.py +emergencydir = $(pythondir)/pyfaf/emergency + +httpdconf_DATA = faf-emergency.conf +httpdconfdir = ${sysconfdir}/httpd/conf.d + +EXTRA_DIST = faf-emergency.conf.in + +faf-emergency.conf: faf-emergency.conf.in + sed -e "s|@PYTHONDIR@|$(pythondir)|g" $< > $@ diff --git a/pyfaf/emergency/__init__.py b/pyfaf/emergency/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyfaf/emergency/emergency.wsgi b/pyfaf/emergency/emergency.wsgi new file mode 100644 index 0000000..205d796 --- /dev/null +++ b/pyfaf/emergency/emergency.wsgi @@ -0,0 +1,88 @@ +#! /usr/bin/env python + +import os +import cgi +import json +import uuid +import logging + +from wsgiref.simple_server import make_server + +import pyfaf + + +def application(environ, start_response): + """ + WSGI application capable of saving incoming uReports to + directory, to be used as a backup handler. + """ + + def is_post_request(environ): + """ + Return True if this request is valid POST request + with correct content type + """ + + if environ["REQUEST_METHOD"].upper() != "POST": + return False + + content_type = environ.get("CONTENT_TYPE", "application/x-www-form-urlencoded") + return content_type.startswith("multipart/form-data") + + def bad_request(): + """ + Respond with HTTP 400 Bad Reqeust. + """ + + start_response("400 BAD REQUEST", + [("Content-Type", "application/json")]) + return [""] + + def method_not_allowed(): + """ + Respond with HTTP 405 Method Not Allowed. + """ + + start_response("405 METHOD NOT ALLOWED", + [("Content-Type", "application/json"), + ("Allow", "POST")]) + return [""] + + spool_dir = pyfaf.config.get("Report.SpoolDirectory") + + if not is_post_request(environ): + logging.debug('Not a POST request or content type' + ' is not mulitpart/form-data.') + + return method_not_allowed() + + inp = environ["wsgi.input"] + fs = cgi.FieldStorage(fp=inp, environ=environ) + + try: + fs = fs.list.pop() + ureport = fs.file.read() + except (IndexError, AttributeError): + logging.debug('Unable to parse input.') + return bad_request() + + fname = str(uuid.uuid4()) + with open(os.path.join(spool_dir, "incoming", fname), "w") as fil: + fil.write(ureport) + logging.info('Report saved as {0}'.format(fname)) + + status = "202 ACCEPTED" + response_body = json.dumps({"result": False}) + response_headers = [("Content-Type", "application/json"), + ("Content-Length", str(len(response_body)))] + start_response(status, response_headers) + + return [response_body] + +if __name__ == "__main__": + # possible to run as standalone server, + # mainly for debugging purposes + + logging.basicConfig(level=logging.DEBUG) + server = make_server("0.0.0.0", 8000, application) + server.serve_forever() diff --git a/pyfaf/emergency/faf-emergency.conf.in b/pyfaf/emergency/faf-emergency.conf.in new file mode 100644 index 0000000..1a14c15 --- /dev/null +++ b/pyfaf/emergency/faf-emergency.conf.in @@ -0,0 +1,16 @@ +#WSGISocketPrefix /var/spool/faf/wsgi +#WSGIDaemonProcess faf user=faf group=faf processes=1 threads=5 +#WSGIProcessGroup faf +#WSGIScriptAlias /faf @PYTHONDIR@/pyfaf/emergency/emergency.wsgi + +#<Directory "@PYTHONDIR@/pyfaf/emergency/"> +# <IfModule mod_authz_core.c> +# # Apache 2.4 +# Require all granted +# </IfModule> +# <IfModule !mod_authz_core.c> +# # Apache 2.2 +# Order allow,deny +# Allow from all +# </IfModule> +#</Directory>