Example #1
0
def run():
    return [["pass", tangelo.abspath("/blah/yay.html")],
            ["fail", tangelo.abspath("blah/yay.html")],
            ["fail", tangelo.abspath("/../blah/yay.html")],
            ["pass", tangelo.abspath("/~roni/blah")],
            ["fail", tangelo.abspath("/~roni/..")],
            ["fail", tangelo.abspath("~roni/blah")]]
Example #2
0
def run(path=""):
    if len(path) == 0:
        return {"error": "missing required argument 'path'"}

    path = tangelo.abspath(path)
    if path is None:
        return {"error": "illegal web path"}

    try:
        with open(path) as f:
            spec = json.loads(f.read())
    except (OSError, IOError):
        return {"status": "could not open file", "result": {}}
    except ValueError as e:
        return {"error": "could not parse JSON - %s" % (e.message)}

    return {"status": "OK", "result": spec}
Example #3
0
def run(path=""):
    if len(path) == 0:
        return {"error": "missing required argument 'path'"}

    path = tangelo.abspath(path)
    if path is None:
        return {"error": "illegal web path"}

    try:
        with open(path) as f:
            spec = json.loads(f.read())
    except (OSError, IOError):
        return {"status": "could not open file",
                "result": {}}
    except ValueError as e:
        return {"error": "could not parse JSON - %s" % (e.message)}

    return {"status": "OK",
            "result": spec}
Example #4
0
def run(path=""):
    if len(path) == 0:
        return {"error": "missing required argument 'path'"}

    if path[0] not in ["/", "~"]:
        return {"error": "path must refer to an absolute web path"}

    path = tangelo.abspath(path)

    try:
        with open(path) as f:
            spec = json.loads(f.read())
    except (OSError, IOError):
        return {"status": "could not open file",
                "result": {}}
    except ValueError as e:
        return {"error": "could not parse JSON - %s" % (e.message)}

    return {"status": "OK",
            "result": spec}
Example #5
0
    def vtkweb(self, *pargs, **kwargs):
        # TODO(choudhury): Implement a PUT method that expands the pool of
        # usable port numbers.

        # Be sure there was a vtkpython executable specified via the
        # environment, and a value provided for self.here.
        if self.vtkpython is None:
            return json.dumps({ "status": "failed",
                                "reason": "no vtkpython executable was specified at Tangelo startup" })
        if self.here is None:
            return json.dumps({ "status": "failed",
                                "reason": "no 'here' argument provided to Tangelo object constructor" })

        # Get keyword arguments.
        progargs = kwargs.get("progargs", "")
        timeout = kwargs.get("timeout", 0)

        # Convert the positional args to a list.
        pargs = list(pargs)

        # Dispatch a RESTful action.
        method = cherrypy.request.method
        if method == "GET":
            # If there is no key argument, send back a list of all the keys.
            if len(pargs) == 0:
                response = self.vtkweb_processes.keys()
            else:
                # Extract the key argument.
                key = pargs[0]

                # Check for the key in the process table.
                if key not in self.vtkweb_processes:
                    return json.dumps({"status": "failed", "reason": "Requested key not in process table"})

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

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

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

            # Make a report to the user.
            return json.dumps(response)
        elif method == "POST":
            if len(pargs) == 0:
                return json.dumps({"status": "incomplete", "reason": "missing path to vtkweb script"})

            # Form the web path from the pargs components
            progpath = os.path.sep.join(pargs)

            # Verify that all required arguments are present.
            if len(pargs) == 0:
                return json.dumps({"status": "incomplete", "reason": "Missing program URL"})

            # Check the user arguments.
            userargs = progargs.split()
            if "--port" in userargs:
                return json.dumps({"status": "incomplete", "reason": "You may not specify --port in this interface"})

            # Verify that the program path is legal.
            if not tangelo.legal_path(progpath)[0]:
                return json.dumps({"status": "incomplete", "reason": "Illegal program URL"})

            # Obtain a filesystem path to the requested program.
            progfile = tangelo.abspath(progpath)

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

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

            def launch_failure(msg):
                # On launch failure, report the failure to the user.
                return json.dumps({"status": "failed", "reason": msg})

            # Launch the requested process.
            try:
                cmdline = [self.vtkpython, self.here + "/bin/vtkweb-launcher.py", progfile, "--port", str(port)] + userargs
                tangelo.log("starting a vtkweb process: %s" % (" ".join(cmdline)))
                process = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            except OSError as e:
                return launch_failure(e.strerror)
            except IOError as e:
                return launch_failure(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"
            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:
                return json.dumps({ "status": "failed",
                                    "reason": "process startup timed out" })
            except Failed:
                return json.dumps({ "status": "failed",
                                    "reason": "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 = cherrypy.server.socket_host
            if host == "0.0.0.0":
                host = "localhost"
            wshandler = tangelo.websocket.WebSocketRelay(host, port, key)
            cherrypy.tree.mount(tangelo.websocket.WebSocketHandler(),
                                "/%s" % (key),
                                config={"/ws": { "tools.websocket.on": True,
                                                 "tools.websocket.handler_cls": wshandler,
                                                 "tools.websocket.protocols": ["wamp"] } })

            # Log the new process in the process table, including non-blocking
            # stdout and stderr readers.
            self.vtkweb_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 = "ws://%s/%s/ws" % (cherrypy.request.base.split("//")[1], key)
            return json.dumps({"status": "complete", "key": key, "url": url})
        elif method == "DELETE":
            # TODO(choudhury): shut down a vtkweb process by key after a given
            # timeout.

            # Make sure there's a key.
            if len(pargs) == 0:
                return json.dumps({"status": "incomplete", "reason": "'key' argument is REQUIRED"})

            # Extract the key.
            key = pargs[0]
            tangelo.log("shutting down %s" % (key))

            # Check for the key in the process table.
            if key not in self.vtkweb_processes:
                tangelo.log("key not found")
                return json.dumps({"status": "failed", "reason": "no such key in process table"})

            # Terminate the process.
            tangelo.log("terminating process")
            proc = self.vtkweb_processes[key]
            proc["process"].terminate()
            proc["process"].wait()
            tangelo.log("terminated")

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

            return json.dumps({"status": "complete"})
        else:
            raise cherrypy.HTTPError(405, "Method not allowed")