Issues (34)

backends/daemon.py (1 issue)

Severity
1
import ConfigParser
2
import base64
3
import sys
4
import logging
5
import urllib2
6
import tempfile
7
import shutil
8
import os
9
import threading
10
import json
11
import socket
12
from SimpleXMLRPCServer import SimpleXMLRPCServer
13
14
import requests
15
16
17
logging.basicConfig(level=logging.DEBUG)
18
logger = logging.getLogger('FuzzEd')
19
20
backends = {}
21
options = {}
22
23
useTestServer = False
24
25
26
class WorkerThread(threading.Thread):
27
    jobtype = ""
28
    joburl = ""
29
30
    def __init__(self, jobtype, joburl):
31
        self.jobtype = jobtype
32
        self.joburl = joburl
33
        threading.Thread.__init__(self)
34
35
    def sendResult(self, exit_code, file_data=None, file_name=None):
36
        """
37
        :rtype : None
38
        """
39
        results = {'exit_code': exit_code}
40
        if file_data and file_name:
41
            results['file_name'] = file_name
42
            results['file_data'] = base64.b64encode(file_data)
43
        logger.debug("Sending result data to %s" % (self.joburl))
44
        headers = {'content-type': 'application/json'}
45
        r = requests.patch(self.joburl, data=json.dumps(
46
            results), verify=False, headers=headers)
47
        if r.text:
48
            logger.debug("Data sent, response was: " + str(r.text))
49
50
    def run(self):
51
        try:
52
            logger.info("Working for job URL: " + self.joburl)
53
54
            # Create tmp directories
55
            tmpdir = tempfile.mkdtemp()
56
            tmpfile = tempfile.NamedTemporaryFile(dir=tmpdir, delete=False)
57
58
            # Fetch input data and store it
59
            input_data = urllib2.urlopen(self.joburl).read()
60
            tmpfile.write(input_data)
61
#            logger.debug(input_data)
62
            tmpfile.close()
63
64
            # There trick is that we do not need to know the operational details
65
            # of this job here, since the calling convention comes from daemon.ini
66
            # and the input file format is determined by the web server on download.
67
            # Alle backend executables are just expected to follow the same
68
            # command-line pattern as render.py.
69
            cmd = "%s %s %s %s %s" % (backends[self.jobtype]['executable'],
70
                                      tmpfile.name,
71
                                      tmpdir + os.sep +
72
                                      backends[self.jobtype]['output'],
73
                                      tmpdir,
74
                                      backends[self.jobtype]['log_file'])
75
            logger.info("Running " + cmd)
76
            output_file = backends[self.jobtype]['output']
77
78
            # Run command synchronousely and wait for the exit code
79
            exit_code = os.system(cmd)
80
            if exit_code == 0:
81
                logger.info("Exit code 0, preparing result upload")
82
                # multiple result file upload not implemented
83
                assert(not output_file.startswith("*"))
84
                with open(tmpdir + os.sep + output_file, "rb") as fd:
85
                    data = fd.read()
86
                    self.sendResult(0, data, output_file)
87
#                    logger.debug(data)
88
            else:
89
                logger.error("Error on execution: Exit code " + str(exit_code))
90
                logger.error(
91
                    "Saving input file for later reference: /tmp/lastinput.xml")
92
                os.system("cp %s /tmp/lastinput.xml" % tmpfile.name)
93
                self.sendResult(exit_code)
94
95
        except Exception as e:
96
            logger.debug(
97
                'Exception, delivering -1 exit code to frontend: ' + str(e))
98
            self.sendResult(-1)
99
100
        finally:
101
            shutil.rmtree(tmpdir, ignore_errors=True)
0 ignored issues
show
The variable tmpdir does not seem to be defined for all execution paths.
Loading history...
102
103
104
class JobServer(SimpleXMLRPCServer):
105
106
    def __init__(self, conf):
107
        SimpleXMLRPCServer.__init__(
108
            self, (socket.gethostbyname("0.0.0.0"), int(options['backend_daemon_port'])))
109
        self.register_function(self.handle_request, 'start_job')
110
111
    def handle_request(self, jobtype, joburl):
112
        logger.debug("Received %s job at %s" % (jobtype, joburl))
113
        if jobtype not in backends.keys():
114
            logger.error("Unknown job type " + jobtype)
115
            return False
116
        else:
117
            # Start worker thread for this task
118
            worker = WorkerThread(jobtype, joburl)
119
            worker.start()
120
            return True
121
122
123
if __name__ == '__main__':
124
    # Read configuration
125
    assert(len(sys.argv) < 4)
126
    conf = ConfigParser.ConfigParser()
127
    if len(sys.argv) == 1:
128
        # Use default INI file in local directory
129
        conf.readfp(open('./daemon.ini'))
130
    else:
131
        conf.readfp(open(sys.argv[1]))
132
    # Read backends from configuration
133
    for section in conf.sections():
134
        if section.startswith('backend_'):
135
            settings = dict(conf.items(section))
136
            backends[settings['job_kind']] = settings
137
        elif section == 'server':
138
            options = dict(conf.items('server'))
139
    logger.info("Configured backends: " + str(backends.keys()))
140
    logger.info("Options: " + str(options))
141
    # Start server
142
    server = JobServer(conf)
143
    server.serve_forever()
144