Ejemplo n.º 1
0
def initialize():
    global vtkpython
    global weblauncher

    # Get the module config.
    config = tangelo.plugin_config()

    # Raise an error if there's no vtkpython executable.
    vtkpython = config.get("vtkpython", None)
    if not vtkpython:
        msg = "No 'vtkpython' option specified in configuration plugin"
        tangelo.log_warning("VTKWEB", "[initialization] fatal error: %s" % (msg))

        # Construct a run() function that will mask the restful API and just
        # inform the caller about the configuration problem.
        def run():
            tangelo.http_status(400, "Bad Configuration")
            return {"error": msg}

        sys.modules[__name__].__dict__["run"] = run
        return

    vtkpython = tangelo.util.expandpath(vtkpython)
    tangelo.log("VTKWEB", "[initialization] Using vtkpython executable %s" % (vtkpython))

    # Use the "web launcher" included with the plugin.
    weblauncher = os.path.realpath("%s/../include/vtkweb-launcher.py" % (os.path.dirname(__file__)))

    # Initialize a table of VTKWeb processes.
    if tangelo.plugin_store().get("processes") is None:
        tangelo.plugin_store()["processes"] = {}

    # Check to see if a reactor is running already.
    if twisted.internet.reactor.running:
        threads = [t for t in threading.enumerate() if t.name == "tangelo-vtkweb-plugin"]
        if len(threads) > 0:
            tangelo.log_warning(
                "VTKWEB", "[initialization] A reactor started by a previous loading of this plugin is already running"
            )
        else:
            tangelo.log_warning(
                "VTKWEB", "[initialization] A reactor started by someone other than this plugin is already running"
            )
    else:
        # Start the Twisted reactor, but in a separate thread so it doesn't
        # block the CherryPy main loop.  Mark the thread as "daemon" so that
        # when Tangelo's main thread exits, the reactor thread will be killed
        # immediately.
        reactor = threading.Thread(
            target=twisted.internet.reactor.run, kwargs={"installSignalHandlers": False}, name="tangelo-vtkweb-plugin"
        )
        reactor.daemon = True
        reactor.start()

        tangelo.log_info("VTKWEB", "[initialization] Starting Twisted reactor")
Ejemplo n.º 2
0
def initialize():
    global vtkpython
    global weblauncher

    # Get the module config.
    config = tangelo.plugin_config()

    # Raise an error if there's no vtkpython executable.
    vtkpython = config.get("vtkpython", None)
    if not vtkpython:
        msg = "No 'vtkpython' option specified in configuration plugin"
        tangelo.log_warning("VTKWEB", "[initialization] fatal error: %s" % (msg))

        # Construct a run() function that will mask the restful API and just
        # inform the caller about the configuration problem.
        def run():
            tangelo.http_status(400, "Bad Configuration")
            return {"error": msg}

        sys.modules[__name__].__dict__["run"] = run
        return

    vtkpython = tangelo.util.expandpath(vtkpython)
    tangelo.log("VTKWEB", "[initialization] Using vtkpython executable %s" % (vtkpython))

    # Use the "web launcher" included with the plugin.
    weblauncher = os.path.realpath("%s/../include/vtkweb-launcher.py" % (os.path.dirname(__file__)))

    # Initialize a table of VTKWeb processes.
    if tangelo.plugin_store().get("processes") is None:
        tangelo.plugin_store()["processes"] = {}

    # Check to see if a reactor is running already.
    if twisted.internet.reactor.running:
        threads = [t for t in threading.enumerate() if t.name == "tangelo-vtkweb-plugin"]
        if len(threads) > 0:
            tangelo.log_warning("VTKWEB", "[initialization] A reactor started by a previous loading of this plugin is already running")
        else:
            tangelo.log_warning("VTKWEB", "[initialization] A reactor started by someone other than this plugin is already running")
    else:
        # Start the Twisted reactor, but in a separate thread so it doesn't
        # block the CherryPy main loop.  Mark the thread as "daemon" so that
        # when Tangelo's main thread exits, the reactor thread will be killed
        # immediately.
        reactor = threading.Thread(
            target=twisted.internet.reactor.run,
            kwargs={"installSignalHandlers": False},
            name="tangelo-vtkweb-plugin"
        )
        reactor.daemon = True
        reactor.start()

        tangelo.log_info("VTKWEB", "[initialization] Starting Twisted reactor")
Ejemplo n.º 3
0
def get(key=None):

    processes = tangelo.plugin_store()["processes"]

    # If no key was supplied, return list of running processes.
    if key is None:
        return processes.keys()

    # Error for bad key.
    if key not in processes:
        tangelo.http_status(400, "No Such Process Key")
        return {"error": "Requested key not in process table"}

    # Retrieve the process entry.
    rec = processes[key]
    response = {"status": "complete",
                "process": "running",
                "port": rec["port"],
                "stdout": rec["stdout"].readlines(),
                "stderr": rec["stderr"].readlines()}

    # Check the status of the process.
    returncode = rec["process"].poll()
    if returncode is not None:
        # Since the process has ended, delete the process object.
        del processes[key]

        # Fill out the report response.
        response["process"] = "terminated"
        response["returncode"] = returncode

    return response
Ejemplo n.º 4
0
def delete(key=None):
    # TODO(choudhury): shut down a vtkweb process by key after a given timeout.

    processes = tangelo.plugin_store()["processes"]

    if key is None:
        tangelo.http_status(400, "Required Argument Missing")
        return {"error": "'key' argument is required"}

    # Check for the key in the process table.
    if key not in processes:
        tangelo.http_status(400, "Key Not Found")
        return {"error": "Key %s not in process table" % (key)}

    # Terminate the process.
    tangelo.log_info("VTKWEB", "Shutting down process %s" % (key))
    proc = processes[key]
    proc["process"].terminate()
    proc["process"].wait()
    tangelo.log_info("VTKWEB", "Process terminated")

    # Remove the process entry from the table.
    del processes[key]

    return {"key": key}
Ejemplo n.º 5
0
def post(*pargs, **query):
    args = query.get("args", "")
    timeout = float(query.get("timeout", 0))

    processes = tangelo.plugin_store()["processes"]

    if len(pargs) == 0:
        tangelo.http_status(400, "Required Argument Missing")
        return {"error": "No program path was specified"}

    program_url = "/" + "/".join(pargs)

    content = analyze_url(program_url).content
    if content is None or content.type != Content.File:
        tangelo.http_status(404, "Not Found")
        return {"error": "Could not find a script at %s" % (program_url)}
    elif content.path is None:
        tangelo.http_status(403, "Restricted")
        return {"error": "The script at %s is access-restricted"}

    program = content.path

    # Check the user arguments.
    userargs = args.split()
    if "--port" in userargs:
        tangelo.http_status(400, "Illegal Argument")
        return {"error": "You may not specify '--port' among the arguments passed in 'args'"}

    # Obtain an available port.
    port = tangelo.util.get_free_port()

    # Generate a unique key.
    key = tangelo.util.generate_key(processes.keys())

    # Detect http vs. https
    scheme = "ws"
    ssl_key = cherrypy.config.get("server.ssl_private_key")
    ssl_cert = cherrypy.config.get("server.ssl_certificate")

    # Generate command line.
    cmdline = [vtkpython, weblauncher, program, "--port", str(port)] + userargs
    if ssl_key and ssl_cert:
        scheme = "wss"
        cmdline.extend(["--sslKey", ssl_key, "--sslCert", ssl_cert])

    # Launch the requested process.
    tangelo.log_info("VTKWEB", "Starting process: %s" % (" ".join(cmdline)))
    try:
        process = subprocess.Popen(cmdline,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
    except (OSError, IOError) as e:
        tangelo.log_warning("VTKWEB", "Error: could not launch VTKWeb process")
        return {"error": e.strerror}

    # Capture the new process's stdout and stderr streams in
    # non-blocking readers.
    stdout = tangelo.util.NonBlockingReader(process.stdout)
    stderr = tangelo.util.NonBlockingReader(process.stderr)

    # Read from stdout to look for the signal that the process has
    # started properly.
    class FactoryStarted:
        pass

    class Failed:
        pass

    class Timeout:
        pass
    signal = "Starting factory"
    if timeout <= 0:
        timeout = 10
    sleeptime = 0.5
    wait = 0
    saved_lines = []
    try:
        while True:
            lines = stdout.readlines()
            saved_lines += lines
            for line in lines:
                if line == "":
                    # This indicates that stdout has closed without
                    # starting the process.
                    raise Failed()
                elif signal in line:
                    # This means that the server has started.
                    raise FactoryStarted()

            # If neither failure nor success occurred in the last block
            # of lines from stdout, either time out, or try again after
            # a short delay.
            if wait >= timeout:
                raise Timeout()

            wait += sleeptime
            time.sleep(sleeptime)
    except Timeout:
        tangelo.http_status(524, "Timeout")
        return {"error": "Process startup timed out"}
    except Failed:
        tangelo.http_status(500)
        return {"error": "Process did not start up properly",
                "stdout": saved_lines,
                "stderr": stderr.readlines()}
    except FactoryStarted:
        stdout.pushlines(saved_lines)

    # Create a websocket handler path dedicated to this process.
    host = "localhost" if cherrypy.server.socket_host == "0.0.0.0" else cherrypy.server.socket_host
    tangelo.websocket.mount(key, WebSocketRelay(host, port, key), "wamp")

    # Log the new process in the process table, including non-blocking
    # stdout and stderr readers.
    processes[key] = {"port": port,
                      "process": process,
                      "stdout": stdout,
                      "stderr": stderr}

    # Form the websocket URL from the hostname/port used in the
    # request, and the newly generated key.
    url = "%s://%s/ws/%s/ws" % (scheme, cherrypy.request.base.split("//")[1], key)
    return {"key": key,
            "url": url}