From 261eb7fe0f7af0c8a21d36f0afae304557ad6e8c Mon Sep 17 00:00:00 2001 From: Daniel Blankenberg Date: Fri, 8 Mar 2019 12:58:01 -0500 Subject: [PATCH 001/114] Add RealTimeTools. Currently working for docker in local runner. --- .../scripts/entry/analysis/AnalysisRouter.js | 12 +- client/galaxy/scripts/layout/menu.js | 6 + client/galaxy/scripts/mvc/tool/tool-form.js | 20 +- config/datatypes_conf.xml.sample | 1 + config/galaxy.yml.realtime | 32 +++ lib/galaxy/app.py | 3 + lib/galaxy/config.py | 2 + lib/galaxy/datatypes/realtime.py | 76 +++++++ lib/galaxy/jobs/__init__.py | 6 + lib/galaxy/jobs/runners/__init__.py | 16 +- lib/galaxy/jobs/runners/local.py | 87 ++++++++ lib/galaxy/managers/realtime.py | 202 ++++++++++++++++++ lib/galaxy/model/__init__.py | 50 +++++ lib/galaxy/model/mapping.py | 50 +++++ .../migrate/versions/0147_realtime_tool.py | 89 ++++++++ lib/galaxy/tools/__init__.py | 30 ++- lib/galaxy/tools/actions/realtime.py | 15 ++ lib/galaxy/tools/deps/container_classes.py | 21 +- lib/galaxy/tools/deps/dependencies.py | 3 +- lib/galaxy/tools/deps/docker_util.py | 29 +++ lib/galaxy/tools/deps/singularity_util.py | 2 + lib/galaxy/tools/execute.py | 9 + lib/galaxy/tools/parser/interface.py | 5 + lib/galaxy/tools/parser/xml.py | 15 +- .../realtime/rpc/key_type_token_mapping.py | 31 +++ lib/galaxy/webapps/galaxy/api/tools.py | 1 + .../webapps/galaxy/controllers/realtime.py | 148 +++++++++++++ .../webapps/galaxy/controllers/tool_runner.py | 3 +- 28 files changed, 951 insertions(+), 13 deletions(-) create mode 100644 config/galaxy.yml.realtime create mode 100644 lib/galaxy/datatypes/realtime.py create mode 100644 lib/galaxy/managers/realtime.py create mode 100644 lib/galaxy/model/migrate/versions/0147_realtime_tool.py create mode 100644 lib/galaxy/tools/actions/realtime.py create mode 100644 lib/galaxy/web/realtime/rpc/key_type_token_mapping.py create mode 100644 lib/galaxy/webapps/galaxy/controllers/realtime.py diff --git a/client/galaxy/scripts/entry/analysis/AnalysisRouter.js b/client/galaxy/scripts/entry/analysis/AnalysisRouter.js index 2d8b1eb123e..80345e39ae8 100644 --- a/client/galaxy/scripts/entry/analysis/AnalysisRouter.js +++ b/client/galaxy/scripts/entry/analysis/AnalysisRouter.js @@ -72,7 +72,8 @@ export const getAnalysisRouter = Galaxy => "(/)datasets(/)list(/)": "show_datasets", "(/)custom_builds": "show_custom_builds", "(/)datasets/edit": "show_dataset_edit_attributes", - "(/)datasets/error": "show_dataset_error" + "(/)datasets/error": "show_dataset_error", + "(/)realtime/list": "show_realtime_list" }, require_login: ["show_user", "show_user_form", "show_workflows"], @@ -109,6 +110,15 @@ export const getAnalysisRouter = Galaxy => this.page.display(new FormWrapper.View(_.extend(model.get(form_id), { active_tab: "user" }))); }, + show_realtime_list: function() { + this.page.display( + new GridView({ + url_base: `${getAppRoot()}realtime/list`, + active_tab: "analysis" + }) + ); + }, + show_visualizations: function(action_id) { var activeTab = action_id == "list_published" ? "shared" : "user"; this.page.display( diff --git a/client/galaxy/scripts/layout/menu.js b/client/galaxy/scripts/layout/menu.js index 286e4150d99..d32e1ab0b29 100644 --- a/client/galaxy/scripts/layout/menu.js +++ b/client/galaxy/scripts/layout/menu.js @@ -310,6 +310,12 @@ var Collection = Backbone.Collection.extend({ { title: _l("Saved Visualizations"), url: "visualizations/list", + target: "__use_router__", + divider: true, + }, + { + title: _l("Active RealTimeTools"), + url: "realtime/list", target: "__use_router__" } ] diff --git a/client/galaxy/scripts/mvc/tool/tool-form.js b/client/galaxy/scripts/mvc/tool/tool-form.js index f5e11bfeaed..94f108e60b4 100644 --- a/client/galaxy/scripts/mvc/tool/tool-form.js +++ b/client/galaxy/scripts/mvc/tool/tool-form.js @@ -346,6 +346,22 @@ var View = Backbone.View.extend({ return inputs; }, + _getViewResult: function(response) { + if (response.view_result) { + return `
+

+ + There is a RealTimeTool result view available, click here to display. You can also click on the display eye icon () + associated with the RealTimeTool Dataset to view. +

+

+ You may also access all active RealTimeTools from the User menu. +

+
`; + } + return ""; + }, + _templateRow: function(list, title, max = 3) { var blurb = ""; list.sort(function(a, b) { @@ -376,7 +392,9 @@ var View = Backbone.View.extend({ var ninputsText = ninputs > 1 ? `${ninputs} inputs` : `this input`; var noutputsText = noutputs > 1 ? `${noutputs} outputs` : `this output`; var tool_name = this.form.model.get("name"); - return `
+ var view_result = this._getViewResult(response); + return `${view_result} +

Executed ${tool_name} and successfully added ${njobsText} to the queue.

diff --git a/config/datatypes_conf.xml.sample b/config/datatypes_conf.xml.sample index 61d4345f7df..2b068fe2d8c 100644 --- a/config/datatypes_conf.xml.sample +++ b/config/datatypes_conf.xml.sample @@ -170,6 +170,7 @@ + diff --git a/config/galaxy.yml.realtime b/config/galaxy.yml.realtime new file mode 100644 index 00000000000..25eeaa589ff --- /dev/null +++ b/config/galaxy.yml.realtime @@ -0,0 +1,32 @@ +uwsgi: + + http: localhost:8080 + + threads: 8 + + http-raw-body: True + + offload-threads: 8 + + master: true + + module: galaxy.webapps.galaxy.buildapp:uwsgi_app() + + sessions: database/session_map.sqlite + python-raw: lib/galaxy/web/realtime/rpc/key_type_token_mapping.py + route-run: log:dynamic_proxy_prefix ${dynamic_proxy_prefix} + route: ^/realtime-proxy/access/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)(()|(/.*)*)$ goto:realtime + route-run: goto:endendend + route-label: realtime + route: ^/realtime-proxy/access/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)(()|/.*)$ rpcvar:TARGET_HOST rtt_key_type_token_mapper $2 $1 $3 $4 $0 + route: ^/realtime-proxy/access/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)((()|/.*)*)$ rewrite:$4 + route-if: empty:${PATH_INFO} addvar:PATH_INFO=/ + route-run: seturi:${PATH_INFO} + route-if-not: empty:${QUERY_STRING} seturi:${PATH_INFO}?${QUERY_STRING} + route-if-not: empty:${TARGET_HOST} httpdumb:${TARGET_HOST} + route-label: endendend + + +galaxy: + + realtime_prefix: /realtime-proxy/access diff --git a/lib/galaxy/app.py b/lib/galaxy/app.py index 85b854cb339..a252b41bba0 100644 --- a/lib/galaxy/app.py +++ b/lib/galaxy/app.py @@ -16,6 +16,7 @@ from galaxy.managers.collections import DatasetCollectionManager from galaxy.managers.folders import FolderManager from galaxy.managers.histories import HistoryManager from galaxy.managers.libraries import LibraryManager +from galaxy.managers.realtime import RealTimeManager from galaxy.managers.tags import GalaxyTagManager from galaxy.queue_worker import GalaxyQueueWorker from galaxy.tools.cache import ( @@ -216,6 +217,8 @@ class UniverseApplication(config.ConfiguresGalaxyMixin): containers_conf=self.config.containers_conf ) + self.realtime_manager = RealTimeManager(self) + # Configure handling of signals handlers = {} if self.heartbeat: diff --git a/lib/galaxy/config.py b/lib/galaxy/config.py index 43e3afff0ca..7c6fb75b1f1 100644 --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -689,6 +689,8 @@ class Configuration(object): self.dynamic_proxy_golang_docker_address = kwargs.get("dynamic_proxy_golang_docker_address", "unix:///var/run/docker.sock") self.dynamic_proxy_golang_api_key = kwargs.get("dynamic_proxy_golang_api_key", None) + self.realtime_prefix = kwargs.get("realtime_prefix", "/realtime-proxy/access") + # Default chunk size for chunkable datatypes -- 64k self.display_chunk_size = int(kwargs.get('display_chunk_size', 65536)) diff --git a/lib/galaxy/datatypes/realtime.py b/lib/galaxy/datatypes/realtime.py new file mode 100644 index 00000000000..b9ceb5e1bd8 --- /dev/null +++ b/lib/galaxy/datatypes/realtime.py @@ -0,0 +1,76 @@ +"""Binary classes""" +from __future__ import print_function + +import logging + +from text import Html +from galaxy.web import url_for + + +log = logging.getLogger(__name__) + + +class Realtime(Html): + """Class describing a RealtimeTool Default output file""" + file_ext = "realtimetool" + composite_type = 'auto_primary_file' + + def set_peek(self, dataset, is_multi_byte=False): + dataset.blurb = "RealtimeTool" + dataset.peek = 'Finished RealTimeTool' + + + def display_peek(self, dataset): + try: + if not dataset.peek: + self.set_peek(dataset) + return dataset.peek + except Exception: + return "Realtime Tool file" + + def generate_primary_file(self, dataset=None): + """ + This is called only at upload to write the html file + cannot rename the datasets here - they come with the default unfortunately + """ + defined_files = self.get_composite_files(dataset=dataset).items() + rval = ["Files for Composite Dataset (%s)" % (self.file_ext)] + if defined_files: + rval.append("

This composite dataset is composed of the following defined files:

    ") + for composite_name, composite_file in defined_files: + opt_text = '' + if composite_file.optional: + opt_text = ' (optional)' + missing_text = '' + if not os.path.exists(os.path.join(dataset.extra_files_path, composite_name)): + missing_text = ' (missing)' + rval.append('
  • %s%s%s
  • ' % (composite_name, composite_name, opt_text, missing_text)) + rval.append("
") + defined_files = map(lambda x: x[0], defined_files) + extra_files = [] + for (dirpath, dirnames, filenames) in os.walk(dataset.extra_files_path, followlinks=True): + for filename in filenames: + rel_path = os.path.relpath(os.path.join(dirpath, filename), dataset.extra_files_path) + if rel_path not in defined_files: + extra_files.append(rel_path) + if extra_files: + rval.append("

This composite dataset contains these undefined files:

    ") + for rel_path in extra_files: + rval.append('
  • %s
  • ' % (rel_path, rel_path)) + rval.append('
') + if not (defined_files or extra_files): + rval.append("

This composite dataset does not contain any files!