From 5bb07cb01bfe488036d87dbc22b627f7ba4e00ec Mon Sep 17 00:00:00 2001 From: Robert Nishihara Date: Tue, 20 Jun 2017 22:54:21 -0700 Subject: [PATCH] Remove old UI code. (#688) --- webui/.eslintrc.json | 16 - webui/.gitattributes | 1 - webui/.github/ISSUE_TEMPLATE.md | 33 -- webui/.gitignore | 3 - webui/.travis.yml | 19 - webui/README.md | 85 ----- webui/backend/ray_ui.py | 460 ------------------------- webui/bower.json | 26 -- webui/images/favicon.ico | Bin 5430 -> 0 bytes webui/images/manifest/icon-144x144.png | Bin 4726 -> 0 bytes webui/images/manifest/icon-192x192.png | Bin 6533 -> 0 bytes webui/images/manifest/icon-48x48.png | Bin 1692 -> 0 bytes webui/images/manifest/icon-512x512.png | Bin 20929 -> 0 bytes webui/images/manifest/icon-72x72.png | Bin 2412 -> 0 bytes webui/images/manifest/icon-96x96.png | Bin 3257 -> 0 bytes webui/index.html | 107 ------ webui/manifest.json | 20 -- webui/package.json | 13 - webui/polymer.json | 19 - webui/service-worker.js | 15 - webui/src/ray-app.html | 140 -------- webui/src/ray-cluster-health.html | 54 --- webui/src/ray-errors.html | 46 --- webui/src/ray-events.html | 47 --- webui/src/ray-icons.html | 21 -- webui/src/ray-log-files.html | 90 ----- webui/src/ray-objects.html | 57 --- webui/src/ray-overview.html | 138 -------- webui/src/ray-recent-tasks.html | 71 ---- webui/src/ray-tasks.html | 45 --- webui/src/ray-timeline.html | 71 ---- webui/src/ray-view404.html | 35 -- webui/src/recent-tasks.js | 107 ------ webui/src/shared-styles.html | 45 --- webui/sw-precache-config.js | 20 -- webui/test/.eslintrc.json | 10 - webui/test/index.html | 29 -- webui/test/my-view1.html | 47 --- 38 files changed, 1890 deletions(-) delete mode 100644 webui/.eslintrc.json delete mode 100644 webui/.gitattributes delete mode 100644 webui/.github/ISSUE_TEMPLATE.md delete mode 100644 webui/.gitignore delete mode 100644 webui/.travis.yml delete mode 100644 webui/README.md delete mode 100644 webui/backend/ray_ui.py delete mode 100644 webui/bower.json delete mode 100644 webui/images/favicon.ico delete mode 100644 webui/images/manifest/icon-144x144.png delete mode 100644 webui/images/manifest/icon-192x192.png delete mode 100644 webui/images/manifest/icon-48x48.png delete mode 100644 webui/images/manifest/icon-512x512.png delete mode 100644 webui/images/manifest/icon-72x72.png delete mode 100644 webui/images/manifest/icon-96x96.png delete mode 100644 webui/index.html delete mode 100644 webui/manifest.json delete mode 100644 webui/package.json delete mode 100644 webui/polymer.json delete mode 100644 webui/service-worker.js delete mode 100644 webui/src/ray-app.html delete mode 100644 webui/src/ray-cluster-health.html delete mode 100644 webui/src/ray-errors.html delete mode 100644 webui/src/ray-events.html delete mode 100644 webui/src/ray-icons.html delete mode 100644 webui/src/ray-log-files.html delete mode 100644 webui/src/ray-objects.html delete mode 100644 webui/src/ray-overview.html delete mode 100644 webui/src/ray-recent-tasks.html delete mode 100644 webui/src/ray-tasks.html delete mode 100644 webui/src/ray-timeline.html delete mode 100644 webui/src/ray-view404.html delete mode 100644 webui/src/recent-tasks.js delete mode 100644 webui/src/shared-styles.html delete mode 100644 webui/sw-precache-config.js delete mode 100644 webui/test/.eslintrc.json delete mode 100644 webui/test/index.html delete mode 100644 webui/test/my-view1.html diff --git a/webui/.eslintrc.json b/webui/.eslintrc.json deleted file mode 100644 index ce13b20e9..000000000 --- a/webui/.eslintrc.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": ["eslint:recommended", "google"], - "env": { - "browser": true - }, - "plugins": [ - "html" - ], - "rules": { - "no-var": "off", - "new-cap": ["error", { "capIsNewExceptions": ["Polymer"] }] - }, - "globals": { - "Polymer": true - } -} diff --git a/webui/.gitattributes b/webui/.gitattributes deleted file mode 100644 index 212566614..000000000 --- a/webui/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text=auto \ No newline at end of file diff --git a/webui/.github/ISSUE_TEMPLATE.md b/webui/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 38f6726f6..000000000 --- a/webui/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,33 +0,0 @@ - -### Description - - -### Expected outcome - - - -### Actual outcome - - - -### Live Demo - - -### Steps to reproduce - - - -### Browsers Affected - -- [ ] Chrome -- [ ] Firefox -- [ ] Safari 9 -- [ ] Safari 8 -- [ ] Safari 7 -- [ ] Edge -- [ ] IE 11 -- [ ] IE 10 diff --git a/webui/.gitignore b/webui/.gitignore deleted file mode 100644 index 77e759d2c..000000000 --- a/webui/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -bower_components/ -build/ -node_modules/ diff --git a/webui/.travis.yml b/webui/.travis.yml deleted file mode 100644 index 280e8ec22..000000000 --- a/webui/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -language: node_js -sudo: required -dist: trusty -addons: - firefox: latest - apt: - sources: - - google-chrome - packages: - - google-chrome-stable -node_js: - - '6' - - '5' - - '4' -before_script: - - npm install -g bower polymer-cli - - bower install -script: - - xvfb-run npm test diff --git a/webui/README.md b/webui/README.md deleted file mode 100644 index e76880d4b..000000000 --- a/webui/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# The Ray Web UI - -This is Ray's Web UI. It consists of two components: - -* The **frontend** is a [Polymer](https://www.polymer-project.org/1.0/) app that - uses [D3](https://d3js.org/) for visualization. -* The **backend** is a Python 3 websocket server (see `backend/ray_ui.py`) that - connects to Redis and potentially Ray. - -### Prerequisites - -The Ray Web UI requires Python 3. - -Install [polymer-cli](https://github.com/Polymer/polymer-cli): - - pip install aioredis websockets - npm install -g polymer-cli - -### Setup - -The following must be done once. - - cd webui - bower install - -### Start the backend - -First start Ray and note down the address of the Redis server. Then run - - cd webui/backend - python ray_ui.py --redis-address 127.0.0.1:6379 - -where you substitute your Redis address appropriately. - -### Start the frontend development server - -To start the front end, run the following. - - cd webui - polymer serve --open - -The web UI can then be accessed at `http://localhost:8080`. - -### Build - -This command performs HTML, CSS, and JS minification on the application -dependencies, and generates a service-worker.js file with code to pre-cache the -dependencies based on the entrypoint and fragments specified in `polymer.json`. -The minified files are output to the `build/unbundled` folder, and are suitable -for serving from a HTTP/2+Push compatible server. - -In addition the command also creates a fallback `build/bundled` folder, -generated using fragment bundling, suitable for serving from non -H2/push-compatible servers or to clients that do not support H2/Push. - - polymer build - -### Preview the build - -This command serves the minified version of the app at `http://localhost:8080` -in an unbundled state, as it would be served by a push-compatible server: - - polymer serve build/unbundled - -This command serves the minified version of the app at `http://localhost:8080` -generated using fragment bundling: - - polymer serve build/bundled - -### Run tests - -This command will run -[Web Component Tester](https://github.com/Polymer/web-component-tester) against -the browsers currently installed on your machine. - - polymer test - -### Adding a new view - -You can extend the app by adding more views that will be demand-loaded e.g. -based on the route, or to progressively render non-critical sections of the -application. Each new demand-loaded fragment should be added to the list of -`fragments` in the included `polymer.json` file. This will ensure those -components and their dependencies are added to the list of pre-cached components -(and will have bundles created in the fallback `bundled` build). diff --git a/webui/backend/ray_ui.py b/webui/backend/ray_ui.py deleted file mode 100644 index ba62b8577..000000000 --- a/webui/backend/ray_ui.py +++ /dev/null @@ -1,460 +0,0 @@ -import aioredis -import argparse -import asyncio -import binascii -import collections -import datetime -import json -import numpy as np -import time -import websockets - -# Import flatbuffer bindings. -from ray.core.generated.LocalSchedulerInfoMessage import \ - LocalSchedulerInfoMessage - -parser = argparse.ArgumentParser( - description="parse information for the web ui") -parser.add_argument("--redis-address", required=True, type=str, - help="the address to use for redis") - -loop = asyncio.get_event_loop() - -IDENTIFIER_LENGTH = 20 - -# This prefix must match the value defined in ray_redis_module.cc. -DB_CLIENT_PREFIX = b"CL:" - - -def hex_identifier(identifier): - return binascii.hexlify(identifier).decode() - - -def identifier(hex_identifier): - return binascii.unhexlify(hex_identifier) - - -def key_to_hex_identifier(key): - return hex_identifier( - key[(key.index(b":") + 1):(key.index(b":") + IDENTIFIER_LENGTH + 1)]) - - -def timestamp_to_date_string(timestamp): - """Convert a time stamp returned by time.time() to a formatted string.""" - return (datetime.datetime.fromtimestamp(timestamp) - .strftime("%Y/%m/%d %H:%M:%S")) - - -def key_to_hex_identifiers(key): - # Extract worker_id and task_id from key of the form - # prefix:worker_id:task_id. - offset = key.index(b":") + 1 - worker_id = hex_identifier(key[offset:(offset + IDENTIFIER_LENGTH)]) - offset += IDENTIFIER_LENGTH + 1 - task_id = hex_identifier(key[offset:(offset + IDENTIFIER_LENGTH)]) - return worker_id, task_id - - -async def hgetall_as_dict(redis_conn, key): - fields = await redis_conn.execute("hgetall", key) - return {fields[2 * i]: fields[2 * i + 1] for i in range(len(fields) // 2)} - - -# Cache information about the local schedulers. -local_schedulers = {} -errors = [] - - -def duration_to_string(duration): - """Format a duration in seconds as a string. - - Args: - duration (float): The duration in seconds. - - Return: - A more human-readable version of the string (for example, "3.5 hours" or - "93 milliseconds"). - """ - if duration > 3600 * 24: - duration_str = "{0:0.1f} days".format(duration / (3600 * 24)) - elif duration > 3600: - duration_str = "{0:0.1f} hours".format(duration / 3600) - elif duration > 60: - duration_str = "{0:0.1f} minutes".format(duration / 60) - elif duration > 1: - duration_str = "{0:0.1f} seconds".format(duration) - elif duration > 0.001: - duration_str = "{0:0.1f} milliseconds".format(duration * 1000) - else: - duration_str = "{} microseconds".format(int(duration * 1000000)) - return duration_str - - -async def handle_get_statistics(websocket, redis_conn): - cluster_start_time = float(await redis_conn.execute("get", - "redis_start_time")) - start_date = timestamp_to_date_string(cluster_start_time) - - uptime = duration_to_string(time.time() - cluster_start_time) - - client_keys = await redis_conn.execute("keys", "CL:*") - clients = [] - for client_key in client_keys: - client_fields = await hgetall_as_dict(redis_conn, client_key) - clients.append(client_fields) - ip_addresses = list(set([client[b"node_ip_address"].decode("ascii") - for client in clients - if client[b"client_type"] == b"local_scheduler"])) - num_nodes = len(ip_addresses) - reply = {"uptime": uptime, - "start_date": start_date, - "nodes": num_nodes, - "addresses": ip_addresses} - await websocket.send(json.dumps(reply)) - - -async def handle_get_drivers(websocket, redis_conn): - keys = await redis_conn.execute("keys", "Drivers:*") - drivers = [] - for key in keys: - driver_fields = await hgetall_as_dict(redis_conn, key) - driver_info = { - "node ip address": driver_fields[b"node_ip_address"].decode("ascii"), - "name": driver_fields[b"name"].decode("ascii")} - - driver_info["start time"] = timestamp_to_date_string( - float(driver_fields[b"start_time"])) - - if b"end_time" in driver_fields: - duration = (float(driver_fields[b"end_time"]) - - float(driver_fields[b"start_time"])) - else: - duration = time.time() - float(driver_fields[b"start_time"]) - driver_info["duration"] = duration_to_string(duration) - - if b"exception" in driver_fields: - driver_info["status"] = "FAILED" - elif b"end_time" not in driver_fields: - driver_info["status"] = "IN PROGRESS" - else: - driver_info["status"] = "SUCCESS" - - if b"exception" in driver_fields: - driver_info["exception"] = driver_fields[b"exception"].decode("ascii") - - drivers.append(driver_info) - # Sort the drivers by their start times. - reply = sorted(drivers, key=(lambda driver: driver["start time"]))[::-1] - await websocket.send(json.dumps(reply)) - - -async def listen_for_errors(redis_ip_address, redis_port): - pubsub_conn = await aioredis.create_connection( - (redis_ip_address, redis_port), loop=loop) - data_conn = await aioredis.create_connection((redis_ip_address, redis_port), - loop=loop) - - error_pattern = "__keyspace@0__:ErrorKeys" - await pubsub_conn.execute_pubsub("psubscribe", error_pattern) - channel = pubsub_conn.pubsub_patterns[error_pattern] - print("Listening for error messages...") - index = 0 - while (await channel.wait_message()): - await channel.get() - info = await data_conn.execute("lrange", "ErrorKeys", index, -1) - - for error_key in info: - worker, task = key_to_hex_identifiers(error_key) - # TODO(richard): Filter out workers so that only relevant task errors are - # necessary. - result = await data_conn.execute("hget", error_key, "message") - result = result.decode("ascii") - # TODO(richard): Maybe also get rid of the coloring. - errors.append({"driver_id": worker, - "task_id": task, - "error": result}) - index += 1 - - -async def handle_get_errors(websocket): - """Send error messages to the frontend.""" - await websocket.send(json.dumps(errors)) - -node_info = collections.OrderedDict() -worker_info = collections.OrderedDict() - - -async def handle_get_recent_tasks(websocket, redis_conn, num_tasks): - # First update the cache of worker information. - worker_keys = await redis_conn.execute("keys", "Workers:*") - for key in worker_keys: - worker_id = hex_identifier(key[len("Workers:"):]) - if worker_id not in worker_info: - worker_info[worker_id] = await hgetall_as_dict(redis_conn, key) - node_ip_address = (worker_info[worker_id][b"node_ip_address"] - .decode("ascii")) - if node_ip_address not in node_info: - node_info[node_ip_address] = {"workers": []} - node_info[node_ip_address]["workers"].append(worker_id) - - keys = await redis_conn.execute("keys", "event_log:*") - if len(keys) == 0: - # There are no tasks, so send a message to the client saying so. - await websocket.send(json.dumps({"num_tasks": 0})) - else: - timestamps = [] - contents = [] - for key in keys: - content = await redis_conn.execute("lrange", key, "0", "-1") - contents.append(json.loads(content[0].decode())) - timestamps += [timestamp for (timestamp, task, kind, info) - in contents[-1] if task == "ray:task"] - - timestamps.sort() - time_cutoff = timestamps[(-2 * num_tasks):][0] - - max_time = timestamps[-1] - min_time = time_cutoff - (max_time - time_cutoff) * 0.1 - max_time = max_time + (max_time - time_cutoff) * 0.1 - - worker_ids = list(worker_info.keys()) - node_ip_addresses = list(node_info.keys()) - - num_tasks = 0 - task_data = [{"task_data": [], - "num_workers": len(node_info[node_ip_address]["workers"])} - for node_ip_address in node_ip_addresses] - for i in range(len(keys)): - worker_id, task_id = key_to_hex_identifiers(keys[i]) - data = contents[i] - if worker_id not in worker_ids: - # This case should be extremely rare. - raise Exception("A worker ID was not present in the list of worker " - "IDs.") - node_ip_address = (worker_info[worker_id][b"node_ip_address"] - .decode("ascii")) - worker_index = node_info[node_ip_address]["workers"].index(worker_id) - node_index = node_ip_addresses.index(node_ip_address) - - task_times = [timestamp for (timestamp, task, kind, info) in data - if task == "ray:task"] - if task_times[1] <= time_cutoff: - continue - - task_get_arguments_times = [timestamp for (timestamp, task, kind, info) - in data if task == "ray:task:get_arguments"] - task_execute_times = [timestamp for (timestamp, task, kind, info) - in data if task == "ray:task:execute"] - task_store_outputs_times = [timestamp for (timestamp, task, kind, info) - in data if task == "ray:task:store_outputs"] - task_info = { - "task": task_times, - "get_arguments": task_get_arguments_times, - "execute": task_execute_times, - "store_outputs": task_store_outputs_times, - "worker_index": worker_index, - "node_ip_address": node_ip_address, - "task_formatted_time": duration_to_string(task_times[1] - - task_times[0]), - "get_arguments_formatted_time": - duration_to_string(task_get_arguments_times[1] - - task_get_arguments_times[0])} - if len(task_execute_times) == 2: - task_info["execute_formatted_time"] = duration_to_string( - task_execute_times[1] - task_execute_times[0]) - if len(task_store_outputs_times) == 2: - task_info["store_outputs_formatted_time"] = duration_to_string( - task_store_outputs_times[1] - task_store_outputs_times[0]) - task_data[node_index]["task_data"].append(task_info) - num_tasks += 1 - reply = {"min_time": min_time, - "max_time": max_time, - "num_tasks": num_tasks, - "task_data": task_data} - await websocket.send(json.dumps(reply)) - - -async def send_heartbeat_payload(websocket): - """Send heartbeat updates to the frontend every half second.""" - while True: - reply = [] - for local_scheduler_id, local_scheduler in local_schedulers.items(): - current_time = time.time() - local_scheduler_info = { - "local scheduler ID": local_scheduler_id, - "time since heartbeat": - (duration_to_string(current_time - - local_scheduler["last_heartbeat"])), - "time since heartbeat numeric": - str(current_time - local_scheduler["last_heartbeat"]), - "node ip address": local_scheduler["node_ip_address"]} - reply.append(local_scheduler_info) - # Send the payload to the frontend. - await websocket.send(json.dumps(reply)) - # Wait for a little while so as not to overwhelm the frontend. - await asyncio.sleep(0.5) - - -async def send_heartbeats(websocket, redis_conn): - # First update the local scheduler info locally. - client_keys = await redis_conn.execute("keys", "CL:*") - for client_key in client_keys: - client_fields = await hgetall_as_dict(redis_conn, client_key) - if client_fields[b"client_type"] == b"local_scheduler": - local_scheduler_id = hex_identifier(client_fields[b"ray_client_id"]) - local_schedulers[local_scheduler_id] = { - "node_ip_address": client_fields[b"node_ip_address"].decode("ascii"), - "local_scheduler_socket_name": - client_fields[b"local_scheduler_socket_name"].decode("ascii"), - "aux_address": client_fields[b"aux_address"].decode("ascii"), - "last_heartbeat": -1 * np.inf} - - # Subscribe to local scheduler heartbeats. - await redis_conn.execute_pubsub("subscribe", "local_schedulers") - - # Start a method in the background to periodically update the frontend. - asyncio.ensure_future(send_heartbeat_payload(websocket)) - - while True: - msg = await redis_conn.pubsub_channels["local_schedulers"].get() - heartbeat = LocalSchedulerInfoMessage.GetRootAsLocalSchedulerInfoMessage( - msg, 0) - local_scheduler_id_bytes = heartbeat.DbClientId() - local_scheduler_id = hex_identifier(local_scheduler_id_bytes) - if local_scheduler_id not in local_schedulers: - # A new local scheduler has joined the cluster. Ignore it. This won't be - # displayed in the UI until the page is refreshed. - continue - local_schedulers[local_scheduler_id]["last_heartbeat"] = time.time() - - -async def cache_data_from_redis(redis_ip_address, redis_port): - """Open up ports to listen for new updates from Redis.""" - # TODO(richard): A lot of code needs to be ported in order to open new - # websockets. - - asyncio.ensure_future(listen_for_errors(redis_ip_address, redis_port)) - - -async def handle_get_log_files(websocket, redis_conn): - reply = {} - # First get all keys for the log file lists. - log_file_list_keys = await redis_conn.execute("keys", "LOG_FILENAMES:*") - for log_file_list_key in log_file_list_keys: - node_ip_address = log_file_list_key.decode("ascii").split(":")[1] - reply[node_ip_address] = {} - # Get all of the log filenames for this node IP address. - log_filenames = await redis_conn.execute("lrange", log_file_list_key, 0, - -1) - for log_filename in log_filenames: - log_filename_key = "LOGFILE:{}:{}".format(node_ip_address, - log_filename.decode("ascii")) - logfile = await redis_conn.execute("lrange", log_filename_key, 0, -1) - logfile = [line.decode("ascii") for line in logfile] - reply[node_ip_address][log_filename.decode("ascii")] = logfile - - # Send the reply back to the front end. - await websocket.send(json.dumps(reply)) - - -async def serve_requests(websocket, path): - redis_conn = await aioredis.create_connection((redis_ip_address, redis_port), - loop=loop) - while True: - command = json.loads(await websocket.recv()) - print("received command {}".format(command)) - - if command["command"] == "get-statistics": - await handle_get_statistics(websocket, redis_conn) - elif command["command"] == "get-drivers": - await handle_get_drivers(websocket, redis_conn) - elif command["command"] == "get-recent-tasks": - await handle_get_recent_tasks(websocket, redis_conn, command["num"]) - elif command["command"] == "get-errors": - await handle_get_errors(websocket) - elif command["command"] == "get-heartbeats": - await send_heartbeats(websocket, redis_conn) - elif command["command"] == "get-log-files": - await handle_get_log_files(websocket, redis_conn) - - if command["command"] == "get-workers": - result = [] - workers = await redis_conn.execute("keys", "WorkerInfo:*") - for key in workers: - content = await redis_conn.execute("hgetall", key) - worker_id = key_to_hex_identifier(key) - result.append({"worker": worker_id, "export_counter": int(content[1])}) - await websocket.send(json.dumps(result)) - elif command["command"] == "get-clients": - result = [] - clients = await redis_conn.execute("keys", "CL:*") - for key in clients: - content = await redis_conn.execute("hgetall", key) - result.append({"client": hex_identifier(content[1]), - "node_ip_address": content[3].decode(), - "client_type": content[5].decode()}) - await websocket.send(json.dumps(result)) - elif command["command"] == "get-objects": - result = [] - objects = await redis_conn.execute("keys", "OI:*") - for key in objects: - content = await redis_conn.execute("hgetall", key) - result.append({"object_id": hex_identifier(content[1]), - "hash": hex_identifier(content[3]), - "data_size": content[5].decode()}) - await websocket.send(json.dumps(result)) - elif command["command"] == "get-object-info": - # TODO(pcm): Get the object here (have to connect to ray) and ship - # content and type back to webclient. One challenge here is that the - # naive implementation will block the web ui backend, which is not ok if - # it is serving multiple users. - await websocket.send(json.dumps({"object_id": "none"})) - elif command["command"] == "get-tasks": - result = [] - tasks = await redis_conn.execute("keys", "TT:*") - for key in tasks: - content = await redis_conn.execute("hgetall", key) - result.append({"task_id": key_to_hex_identifier(key), - "state": int(content[1]), - "node_id": hex_identifier(content[3])}) - await websocket.send(json.dumps(result)) - elif command["command"] == "get-timeline": - tasks = collections.defaultdict(list) - for key in await redis_conn.execute("keys", "event_log:*"): - worker_id, task_id = key_to_hex_identifiers(key) - content = await redis_conn.execute("lrange", key, "0", "-1") - data = json.loads(content[0].decode()) - begin_and_end_time = [timestamp for (timestamp, task, kind, info) - in data if task == "ray:task"] - tasks[worker_id].append({"task_id": task_id, - "start_task": min(begin_and_end_time), - "end_task": max(begin_and_end_time)}) - await websocket.send(json.dumps(tasks)) - elif command["command"] == "get-events": - result = [] - for key in await redis_conn.execute("keys", "event_log:*"): - worker_id, task_id = key_to_hex_identifiers(key) - answer = await redis_conn.execute("lrange", key, "0", "-1") - assert len(answer) == 1 - events = json.loads(answer[0].decode()) - result.extend([{"worker_id": worker_id, - "task_id": task_id, - "time": event[0], - "type": event[1]} for event in events]) - await websocket.send(json.dumps(result)) - -if __name__ == "__main__": - args = parser.parse_args() - redis_address = args.redis_address.split(":") - redis_ip_address, redis_port = redis_address[0], int(redis_address[1]) - - # The port here must match the value used by the frontend to connect over - # websockets. TODO(richard): Automatically increment the port if it is - # already taken. - port = 8888 - - loop.run_until_complete(cache_data_from_redis(redis_ip_address, redis_port)) - - start_server = websockets.serve(serve_requests, "localhost", port) - loop.run_until_complete(start_server) - loop.run_forever() diff --git a/webui/bower.json b/webui/bower.json deleted file mode 100644 index 448b8dfff..000000000 --- a/webui/bower.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "polymer-starter-kit", - "authors": [ - "The Polymer Authors" - ], - "license": "https://polymer.github.io/LICENSE.txt", - "dependencies": { - "app-layout": "PolymerElements/app-layout#^0.10.0", - "app-route": "PolymerElements/app-route#^0.9.0", - "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0", - "iron-icon": "PolymerElements/iron-icon#^1.0.0", - "iron-iconset-svg": "PolymerElements/iron-iconset-svg#^1.0.0", - "iron-localstorage": "PolymerElements/iron-localstorage#^1.0.0", - "iron-media-query": "PolymerElements/iron-media-query#^1.0.0", - "iron-pages": "PolymerElements/iron-pages#^1.0.0", - "iron-selector": "PolymerElements/iron-selector#^1.0.0", - "paper-icon-button": "PolymerElements/paper-icon-button#~1.1.0", - "polymer": "Polymer/polymer#^1.6.0", - "vaadin-grid": "^1.2.1", - "google-chart": "^1.1.1" - }, - "devDependencies": { - "web-component-tester": "^4.0.0" - }, - "private": true -} diff --git a/webui/images/favicon.ico b/webui/images/favicon.ico deleted file mode 100644 index 9cd897ef45bace770f37cfc5ff0a1aad7e3845cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5430 zcmeHLO-x)>6n+@HU}4g6Q?-;biz=lhW)#b63`1l5iI$EjR7x$576h$7G$ECUnT~Bz zBoP$TQo;DzMuciD3lkTzcsn;FcF~wLF=iG-SyW8ScD!V;9C+lTPyW z+_`hk{m#AT+pBk&@UdwuzqnfqvGngO2TrIv*h0_)KU(0I3y#J$= zs<;zy{^|QF`y0+o?tf&(CJr~==n}t{FLy@)o5)V>pUi%>uTp*!3H#)k=6cm$yR16u zSJbh+t~>CeyN$oYt`hil>AS;f@xopO`=r=W!gdSb0_y!;C)HcKZz$Lj--;gWTeDNG zv?W|%i~RP}o13P;j&D}Pxt50c4-dJkCpuk~YA%4__eUiDs9AT~iI>%tUjk&~} zVs0_VV4sxrMZ&(4xd}U+r;vJY{GGWhdCeS$eM$bxI=5R|mQw!&d}nHaCP3bqrE`?||_XT0mTio?5)%|viFVnHZpi@OyI0`6d>oK4U??qT@s?mBgFdq#o32(LEi z8W0b%1NSVzcVZovUQ>%-KEv7&4zdZF1HS{P!Nyb(cYEE6>VHAkpETYd{qCCf;0*&g zlRtZ*MQW2{>XsXWH%4yai>Ip8D?6?geCUZ5>K|)hDrgOGr-Xm$n@+WO?s?&-EY!Rp zTh4(uE&PE+BPK^E)Q5j8X3+0Bip` z*x!t7XMbUf9woRVUfoyUcff~Yeyt1MK9l=6xJTc~bN@ubTu;W}woU%bSVa0G%82NJ z+Y$a>1|Gs0RErNe_)<74@L!=1LtT0H9l}kS0}6VwF1SNLPW$lrlh}K$$?s4Pym8?c z`D5Y%;V-&l;AR|D@<0){_)(;5_>dxhW^|{?En{-0g;(TnsVa4;ZF5@IFy)EP{4Dv| zyZP~6y!1ZO_V-VpNM}asgwvF1#DOs9a87mT8yf@p(L08v|1>50$&-ovtm&(axfkw9 z_Gd_P$4mHeRyt;r&4FhM-jvDBG4qkJVT^>A68V#2J~O5UuZ-i=0l!o50XedOT=Dps zJ0*Dqz7UWTA5I}3hq?>*@`~^?cLC@;CB#n;WTBAZQf(J)(Txi zSW8$_Ol}#Y2HJqsBQ060ShJ*l`J|4G*!v}Gr0D}|C~GOenp)yFYcRlCthrgES*zJM zv3C2o(|$hxcW}pqpS=Tn2#=pK@IHb&CH(ABq-SIA!XC!(q3CY$ItJ9ZyfmJ>`hH>j@iSqmxT`{ pe*c{x@F4=Oz<#=%9ryzKb@uY*Z0^qgKmH#3x8h{{SHOQV{0rSlwbuXu diff --git a/webui/images/manifest/icon-144x144.png b/webui/images/manifest/icon-144x144.png deleted file mode 100644 index c09f0ce58040aacf9e3402fb32df4feede5643e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4726 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 diff --git a/webui/images/manifest/icon-192x192.png b/webui/images/manifest/icon-192x192.png deleted file mode 100644 index e74d22e099e3646cbff0444041740f091cedf32f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6533 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 O0qE-(Yu9KwKl>kHd!{b{ diff --git a/webui/images/manifest/icon-48x48.png b/webui/images/manifest/icon-48x48.png deleted file mode 100644 index 15bb11e279f99a50c47433cde827719d88aa725a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1692 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 Tb^mUTO^N;PgLdUM%*6i!9dCDB diff --git a/webui/images/manifest/icon-72x72.png b/webui/images/manifest/icon-72x72.png deleted file mode 100644 index c0af86565fb21bf879daa10d6ec9acbaa779b2dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2412 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 diff --git a/webui/index.html b/webui/index.html deleted file mode 100644 index 5ff9cdc8c..000000000 --- a/webui/index.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - - - - - Ray UI - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/webui/manifest.json b/webui/manifest.json deleted file mode 100644 index 1c787ff45..000000000 --- a/webui/manifest.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "My App", - "short_name": "My App", - "start_url": "/?homescreen=1", - "display": "standalone", - "theme_color": "#3f51b5", - "background_color": "#3f51b5", - "icons": [ - { - "src": "images/manifest/icon-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "images/manifest/icon-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ] -} diff --git a/webui/package.json b/webui/package.json deleted file mode 100644 index f851c4de3..000000000 --- a/webui/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "polymer-starter-kit", - "license": "BSD-3-Clause", - "devDependencies": { - "eslint": "^3.12.0", - "eslint-config-google": "^0.7.1", - "eslint-plugin-html": "^1.7.0" - }, - "scripts": { - "lint": "eslint . --ext js,html; exit 0;", - "test": "npm run lint && polymer test" - } -} diff --git a/webui/polymer.json b/webui/polymer.json deleted file mode 100644 index 8fe1ac481..000000000 --- a/webui/polymer.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "entrypoint": "index.html", - "shell": "src/my-app.html", - "fragments": [ - "src/my-overview.html", - "src/my-objects.html", - "src/my-view3.html", - "src/my-view404.html" - ], - "sourceGlobs": [ - "src/**/*", - "images/**/*", - "bower.json" - ], - "includeDependencies": [ - "manifest.json", - "bower_components/webcomponentsjs/webcomponents-lite.min.js" - ] -} diff --git a/webui/service-worker.js b/webui/service-worker.js deleted file mode 100644 index 93e9d49d7..000000000 --- a/webui/service-worker.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @license - * Copyright (c) 2016 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt - */ - -/* eslint no-console: ["error", { allow: ["info"] }] */ - -console.info( - 'Service worker disabled for development, will be generated at build time.' -); diff --git a/webui/src/ray-app.html b/webui/src/ray-app.html deleted file mode 100644 index 1eaa2b6fd..000000000 --- a/webui/src/ray-app.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/webui/src/ray-cluster-health.html b/webui/src/ray-cluster-health.html deleted file mode 100644 index dbadd81c1..000000000 --- a/webui/src/ray-cluster-health.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - diff --git a/webui/src/ray-errors.html b/webui/src/ray-errors.html deleted file mode 100644 index c671b4beb..000000000 --- a/webui/src/ray-errors.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - diff --git a/webui/src/ray-events.html b/webui/src/ray-events.html deleted file mode 100644 index 17ebece1e..000000000 --- a/webui/src/ray-events.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - diff --git a/webui/src/ray-icons.html b/webui/src/ray-icons.html deleted file mode 100644 index 8b93b1bc2..000000000 --- a/webui/src/ray-icons.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/webui/src/ray-log-files.html b/webui/src/ray-log-files.html deleted file mode 100644 index 94bd3fbe0..000000000 --- a/webui/src/ray-log-files.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - diff --git a/webui/src/ray-objects.html b/webui/src/ray-objects.html deleted file mode 100644 index 47ee968ea..000000000 --- a/webui/src/ray-objects.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - diff --git a/webui/src/ray-overview.html b/webui/src/ray-overview.html deleted file mode 100644 index 5df612c70..000000000 --- a/webui/src/ray-overview.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - diff --git a/webui/src/ray-recent-tasks.html b/webui/src/ray-recent-tasks.html deleted file mode 100644 index 7ae100f89..000000000 --- a/webui/src/ray-recent-tasks.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - diff --git a/webui/src/ray-tasks.html b/webui/src/ray-tasks.html deleted file mode 100644 index 4c8c9a0a0..000000000 --- a/webui/src/ray-tasks.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - diff --git a/webui/src/ray-timeline.html b/webui/src/ray-timeline.html deleted file mode 100644 index 49375a108..000000000 --- a/webui/src/ray-timeline.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - diff --git a/webui/src/ray-view404.html b/webui/src/ray-view404.html deleted file mode 100644 index 1733afba5..000000000 --- a/webui/src/ray-view404.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - diff --git a/webui/src/recent-tasks.js b/webui/src/recent-tasks.js deleted file mode 100644 index 443847d62..000000000 --- a/webui/src/recent-tasks.js +++ /dev/null @@ -1,107 +0,0 @@ -RecentTasks = function(all_recent_tasks_elem) { - var self = this; - - var verticalPadding = 10; - var barHeight = 25; - - var all_recent_tasks_div = d3.select(all_recent_tasks_elem); - - this.generate_task_info = function(d) { - return "
Total Time: " + d.task_formatted_time + "
" + - "
Time Getting Arguments: " + d.get_arguments_formatted_time + "
" + - "
Time in Execution: " + d.execute_formatted_time + "
" + - "
Time Storing Outputs: " + d.store_outputs_formatted_time + "
"; - } - - this.draw_new_node_tasks = function(all_task_info, task_info, width, svg, info) { - var height = task_info.num_workers * barHeight + 2 * verticalPadding; - - svg.attr("width", width) - .attr("height", height); - - var borderPath = svg.append("rect") - .attr("x", 0) - .attr("y", 0) - .attr("height", height) - .attr("width", width) - .style("stroke", "black") - .style("fill", "none") - .style("stroke-width", 1); - - var x = d3.scaleLinear() - .domain([all_task_info.min_time, all_task_info.max_time]) - .range([-1, width + 1]); - - var task_rects = svg.append("g").attr("class", "task_rects"); - var get_arguments_rects = svg.append("g").attr("class", "get_arguments_rects"); - var execute_rects = svg.append("g").attr("class", "execute_rects"); - var store_outputs_rects = svg.append("g").attr("class", "store_outputs_rects"); - - task_rects.selectAll("rect") - .data(task_info.task_data) - .enter() - .append("rect") - .attr("x", function (d) { return x(d.task[0]); }) - .attr("y", function (d) { return verticalPadding + d.worker_index * barHeight; }) - .attr("width", function (d) { return x(d.task[1]) - x(d.task[0]); }) - .attr("height", function (d) { return barHeight - 1; }) - .attr("fill", "orange") - .attr("id", function (d) { d.store_outputs[1]; }) - .on("click", function(d, i) { - info.html(self.generate_task_info(d)); - }) - - get_arguments_rects.selectAll("rect") - .data(task_info.task_data) - .enter() - .append("rect") - .attr("x", function (d) { return x(d.get_arguments[0]); }) - .attr("y", function (d) { return verticalPadding + d.worker_index * barHeight + 1; }) - .attr("width", function (d) { return x(d.get_arguments[1]) - x(d.get_arguments[0]); }) - .attr("height", function (d) { return barHeight - 3; }) - .attr("fill", "black") - .on("click", function(d, i) { - info.html(self.generate_task_info(d)); - }) - - execute_rects.selectAll("rect") - .data(task_info.task_data) - .enter() - .append("rect") - .attr("x", function (d) { return x(d.execute[0]); }) - .attr("y", function (d) { return verticalPadding + d.worker_index * barHeight + 1; }) - .attr("width", function (d) { return x(d.execute[1]) - x(d.execute[0]); }) - .attr("height", function (d) { return barHeight - 3; }) - .attr("fill", "blue") - .on("click", function(d, i) { - info.html(self.generate_task_info(d)); - }) - - store_outputs_rects.selectAll("rect") - .data(task_info.task_data) - .enter() - .append("rect") - .attr("x", function (d) { return x(d.store_outputs[0]); }) - .attr("y", function (d) { return verticalPadding + d.worker_index * barHeight + 1; }) - .attr("width", function (d) { return x(d.store_outputs[1]) - x(d.store_outputs[0]); }) - .attr("height", function (d) { return barHeight - 3; }) - .attr("fill", "green") - .on("click", function(d, i) { - info.html(self.generate_task_info(d)); - }) - } - - this.draw_new_tasks = function(all_task_info, width) { - // Call draw_new_node_tasks once for each node. - for (i = 0; i < all_task_info.task_data.length; i++) { - var new_svg = all_recent_tasks_div.append("svg"); - var info = all_recent_tasks_div.append("div"); - this.draw_new_node_tasks(all_task_info, all_task_info.task_data[i], width, new_svg, info); - } - } - - this.erase = function() { - all_recent_tasks_div.selectAll("svg").remove(); - all_recent_tasks_div.selectAll("div").remove(); - } -} diff --git a/webui/src/shared-styles.html b/webui/src/shared-styles.html deleted file mode 100644 index 5e4f2491d..000000000 --- a/webui/src/shared-styles.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - diff --git a/webui/sw-precache-config.js b/webui/sw-precache-config.js deleted file mode 100644 index 301bb56b0..000000000 --- a/webui/sw-precache-config.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @license - * Copyright (c) 2016 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt - */ - -/* eslint-env node */ - -module.exports = { - staticFileGlobs: [ - '/index.html', - '/manifest.json', - '/bower_components/webcomponentsjs/webcomponents-lite.min.js', - ], - navigateFallback: 'index.html', -}; diff --git a/webui/test/.eslintrc.json b/webui/test/.eslintrc.json deleted file mode 100644 index 72c940381..000000000 --- a/webui/test/.eslintrc.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "env": { - "mocha": true - }, - "globals": { - "assert": false, - "fixture": false, - "WCT": false - } -} diff --git a/webui/test/index.html b/webui/test/index.html deleted file mode 100644 index 8678e3845..000000000 --- a/webui/test/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - Tests - - - - - - - diff --git a/webui/test/my-view1.html b/webui/test/my-view1.html deleted file mode 100644 index bcb79628e..000000000 --- a/webui/test/my-view1.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - my-view1 - - - - - - - - - - - - - - -