Loading services/webapp/code/rosetta/core_app/models.py +5 −1 Original line number Diff line number Diff line Loading @@ -335,6 +335,10 @@ class Task(models.Model): color_map_index = string_int_hash % len(color_map) return color_map[color_map_index] @property def direct_link(self): return '{}/t/{}'.format(settings.DJANGO_PUBLIC_HTTP_HOST, self.id) #========================= Loading services/webapp/code/rosetta/core_app/templates/components/task.html +11 −4 Original line number Diff line number Diff line Loading @@ -80,10 +80,8 @@ </div> {% if data.task %} <div style="float:left; max-width:450px"> <table class="dashboard" style="margin:10px"> <!-- <tr> Loading Loading @@ -152,6 +150,12 @@ </tr> {% endif %} <tr> <td style="padding-right:0"><b>Direct link</b> <td>{% if task.status == "running" %}<a href="{{ task.direct_link }}">{{ task.direct_link }}</a>{% else %}N.A. (task not running) {% endif %}</td> </tr> <!-- <tr> <td><b>Operations</b></td> Loading Loading @@ -184,7 +188,10 @@ --> </table> <p style="margin-left:12px; font-size:0.9em; color:#484848"> <i class="fa fa-info-circle" style="color:#337ab7"></i> You can share a direct link with other people, but remember that if you have no authentication in place anyone will be able to access. </p> </div> Loading services/webapp/code/rosetta/core_app/utils.py +68 −0 Original line number Diff line number Diff line Loading @@ -489,6 +489,74 @@ def hash_string_to_int(string): #================================ # Tunnel setup #================================ def setup_tunnel(task): # Importing here instead of on top avoids circular dependencies problems when loading booleanize in settings from .models import Task, KeyPair, TaskStatuses # If there is no tunnel port allocated yet, find one if not task.tunnel_port: # Get a free port fot the tunnel: allocated_tunnel_ports = [] for other_task in Task.objects.all(): if other_task.tunnel_port and not other_task.status in [TaskStatuses.exited, TaskStatuses.stopped]: allocated_tunnel_ports.append(other_task.tunnel_port) for port in range(7000, 7006): if not port in allocated_tunnel_ports: tunnel_port = port break if not tunnel_port: logger.error('Cannot find a free port for the tunnel for task "{}"'.format(task)) raise ErrorMessage('Cannot find a free port for the tunnel to the task') task.tunnel_port = tunnel_port task.save() # Check if the tunnel is active and if not create it logger.debug('Checking if task "{}" has a running tunnel'.format(task)) out = os_shell('ps -ef | grep ":{}:{}:{}" | grep -v grep'.format(task.tunnel_port, task.ip, task.port), capture=True) if out.exit_code == 0: logger.debug('Task "{}" has a running tunnel, using it'.format(task)) else: logger.debug('Task "{}" has no running tunnel, creating it'.format(task)) # Get user keys user_keys = KeyPair.objects.get(user=task.user, default=True) # Tunnel command if task.computing.type == 'remotehop': # Get computing params first_host = task.computing.get_conf_param('first_host') first_user = task.computing.get_conf_param('first_user') #second_host = task.computing.get_conf_param('second_host') #second_user = task.computing.get_conf_param('second_user') #setup_command = task.computing.get_conf_param('setup_command') #base_port = task.computing.get_conf_param('base_port') tunnel_command= 'ssh -4 -i {} -o StrictHostKeyChecking=no -nNT -L 0.0.0.0:{}:{}:{} {}@{} & '.format(user_keys.private_key_file, task.tunnel_port, task.ip, task.port, first_user, first_host) else: tunnel_command= 'ssh -4 -o StrictHostKeyChecking=no -nNT -L 0.0.0.0:{}:{}:{} localhost & '.format(task.tunnel_port, task.ip, task.port) background_tunnel_command = 'nohup {} >/dev/null 2>&1 &'.format(tunnel_command) # Log logger.debug('Opening tunnel with command: {}'.format(background_tunnel_command)) # Execute subprocess.Popen(background_tunnel_command, shell=True) Loading services/webapp/code/rosetta/core_app/views.py +18 −66 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ from django.http import HttpResponse, HttpResponseRedirect from django.contrib.auth.models import User from django.shortcuts import redirect from .models import Profile, LoginToken, Task, TaskStatuses, Container, Computing, KeyPair, ComputingSysConf, ComputingUserConf from .utils import send_email, format_exception, timezonize, os_shell, booleanize, debug_param, get_tunnel_host, random_username from .utils import send_email, format_exception, timezonize, os_shell, booleanize, debug_param, get_tunnel_host, random_username, setup_tunnel from .decorators import public_view, private_view from .exceptions import ErrorMessage Loading Loading @@ -361,65 +361,10 @@ def tasks(request): elif action=='connect': # If there is no tunnel port allocated yet, find one if not task.tunnel_port: # First ensure that the tunnel is setu up setup_tunnel(task) # Get a free port fot the tunnel: allocated_tunnel_ports = [] for other_task in Task.objects.all(): if other_task.tunnel_port and not other_task.status in [TaskStatuses.exited, TaskStatuses.stopped]: allocated_tunnel_ports.append(other_task.tunnel_port) for port in range(7000, 7006): if not port in allocated_tunnel_ports: tunnel_port = port break if not tunnel_port: logger.error('Cannot find a free port for the tunnel for task "{}"'.format(task)) raise ErrorMessage('Cannot find a free port for the tunnel to the task') task.tunnel_port = tunnel_port task.save() # Check if the tunnel is active and if not create it logger.debug('Checking if task "{}" has a running tunnel'.format(task)) out = os_shell('ps -ef | grep ":{}:{}:{}" | grep -v grep'.format(task.tunnel_port, task.ip, task.port), capture=True) if out.exit_code == 0: logger.debug('Task "{}" has a running tunnel, using it'.format(task)) else: logger.debug('Task "{}" has no running tunnel, creating it'.format(task)) # Get user keys user_keys = KeyPair.objects.get(user=task.user, default=True) # Tunnel command if task.computing.type == 'remotehop': # Get computing params first_host = task.computing.get_conf_param('first_host') first_user = task.computing.get_conf_param('first_user') #second_host = task.computing.get_conf_param('second_host') #second_user = task.computing.get_conf_param('second_user') #setup_command = task.computing.get_conf_param('setup_command') #base_port = task.computing.get_conf_param('base_port') tunnel_command= 'ssh -4 -i {} -o StrictHostKeyChecking=no -nNT -L 0.0.0.0:{}:{}:{} {}@{} & '.format(user_keys.private_key_file, task.tunnel_port, task.ip, task.port, first_user, first_host) else: tunnel_command= 'ssh -4 -o StrictHostKeyChecking=no -nNT -L 0.0.0.0:{}:{}:{} localhost & '.format(task.tunnel_port, task.ip, task.port) background_tunnel_command = 'nohup {} >/dev/null 2>&1 &'.format(tunnel_command) # Log logger.debug('Opening tunnel with command: {}'.format(background_tunnel_command)) # Execute subprocess.Popen(background_tunnel_command, shell=True) # Ok, now redirect to the task through the tunnel # Then, redirect to the task through the tunnel tunnel_host = get_tunnel_host() return redirect('http://{}:{}'.format(tunnel_host,task.tunnel_port)) Loading Loading @@ -965,15 +910,22 @@ def edit_computing_conf(request): #========================= # Sharable link handler #========================= @public_view def sharable_link_handler(request, id): # Get the task task = Task.objects.get(uuid__startswith=id) # First ensure that the tunnel is setu up setup_tunnel(task) # Then, redirect to the task through the tunnel tunnel_host = get_tunnel_host() return redirect('http://{}:{}'.format(tunnel_host,task.tunnel_port)) Loading services/webapp/code/rosetta/urls.py +3 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,9 @@ urlpatterns = [ url(r'^containers/$', core_app_views.containers), url(r'^add_container/$', core_app_views.add_container), # Sharable link for tasks url(r'^t/(?P<id>\w{0,36})/$', core_app_views.sharable_link_handler), # Modules path('admin/', admin.site.urls), path('api/v1/doc/', get_swagger_view(title="Swagger Documentation")), Loading Loading
services/webapp/code/rosetta/core_app/models.py +5 −1 Original line number Diff line number Diff line Loading @@ -335,6 +335,10 @@ class Task(models.Model): color_map_index = string_int_hash % len(color_map) return color_map[color_map_index] @property def direct_link(self): return '{}/t/{}'.format(settings.DJANGO_PUBLIC_HTTP_HOST, self.id) #========================= Loading
services/webapp/code/rosetta/core_app/templates/components/task.html +11 −4 Original line number Diff line number Diff line Loading @@ -80,10 +80,8 @@ </div> {% if data.task %} <div style="float:left; max-width:450px"> <table class="dashboard" style="margin:10px"> <!-- <tr> Loading Loading @@ -152,6 +150,12 @@ </tr> {% endif %} <tr> <td style="padding-right:0"><b>Direct link</b> <td>{% if task.status == "running" %}<a href="{{ task.direct_link }}">{{ task.direct_link }}</a>{% else %}N.A. (task not running) {% endif %}</td> </tr> <!-- <tr> <td><b>Operations</b></td> Loading Loading @@ -184,7 +188,10 @@ --> </table> <p style="margin-left:12px; font-size:0.9em; color:#484848"> <i class="fa fa-info-circle" style="color:#337ab7"></i> You can share a direct link with other people, but remember that if you have no authentication in place anyone will be able to access. </p> </div> Loading
services/webapp/code/rosetta/core_app/utils.py +68 −0 Original line number Diff line number Diff line Loading @@ -489,6 +489,74 @@ def hash_string_to_int(string): #================================ # Tunnel setup #================================ def setup_tunnel(task): # Importing here instead of on top avoids circular dependencies problems when loading booleanize in settings from .models import Task, KeyPair, TaskStatuses # If there is no tunnel port allocated yet, find one if not task.tunnel_port: # Get a free port fot the tunnel: allocated_tunnel_ports = [] for other_task in Task.objects.all(): if other_task.tunnel_port and not other_task.status in [TaskStatuses.exited, TaskStatuses.stopped]: allocated_tunnel_ports.append(other_task.tunnel_port) for port in range(7000, 7006): if not port in allocated_tunnel_ports: tunnel_port = port break if not tunnel_port: logger.error('Cannot find a free port for the tunnel for task "{}"'.format(task)) raise ErrorMessage('Cannot find a free port for the tunnel to the task') task.tunnel_port = tunnel_port task.save() # Check if the tunnel is active and if not create it logger.debug('Checking if task "{}" has a running tunnel'.format(task)) out = os_shell('ps -ef | grep ":{}:{}:{}" | grep -v grep'.format(task.tunnel_port, task.ip, task.port), capture=True) if out.exit_code == 0: logger.debug('Task "{}" has a running tunnel, using it'.format(task)) else: logger.debug('Task "{}" has no running tunnel, creating it'.format(task)) # Get user keys user_keys = KeyPair.objects.get(user=task.user, default=True) # Tunnel command if task.computing.type == 'remotehop': # Get computing params first_host = task.computing.get_conf_param('first_host') first_user = task.computing.get_conf_param('first_user') #second_host = task.computing.get_conf_param('second_host') #second_user = task.computing.get_conf_param('second_user') #setup_command = task.computing.get_conf_param('setup_command') #base_port = task.computing.get_conf_param('base_port') tunnel_command= 'ssh -4 -i {} -o StrictHostKeyChecking=no -nNT -L 0.0.0.0:{}:{}:{} {}@{} & '.format(user_keys.private_key_file, task.tunnel_port, task.ip, task.port, first_user, first_host) else: tunnel_command= 'ssh -4 -o StrictHostKeyChecking=no -nNT -L 0.0.0.0:{}:{}:{} localhost & '.format(task.tunnel_port, task.ip, task.port) background_tunnel_command = 'nohup {} >/dev/null 2>&1 &'.format(tunnel_command) # Log logger.debug('Opening tunnel with command: {}'.format(background_tunnel_command)) # Execute subprocess.Popen(background_tunnel_command, shell=True) Loading
services/webapp/code/rosetta/core_app/views.py +18 −66 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ from django.http import HttpResponse, HttpResponseRedirect from django.contrib.auth.models import User from django.shortcuts import redirect from .models import Profile, LoginToken, Task, TaskStatuses, Container, Computing, KeyPair, ComputingSysConf, ComputingUserConf from .utils import send_email, format_exception, timezonize, os_shell, booleanize, debug_param, get_tunnel_host, random_username from .utils import send_email, format_exception, timezonize, os_shell, booleanize, debug_param, get_tunnel_host, random_username, setup_tunnel from .decorators import public_view, private_view from .exceptions import ErrorMessage Loading Loading @@ -361,65 +361,10 @@ def tasks(request): elif action=='connect': # If there is no tunnel port allocated yet, find one if not task.tunnel_port: # First ensure that the tunnel is setu up setup_tunnel(task) # Get a free port fot the tunnel: allocated_tunnel_ports = [] for other_task in Task.objects.all(): if other_task.tunnel_port and not other_task.status in [TaskStatuses.exited, TaskStatuses.stopped]: allocated_tunnel_ports.append(other_task.tunnel_port) for port in range(7000, 7006): if not port in allocated_tunnel_ports: tunnel_port = port break if not tunnel_port: logger.error('Cannot find a free port for the tunnel for task "{}"'.format(task)) raise ErrorMessage('Cannot find a free port for the tunnel to the task') task.tunnel_port = tunnel_port task.save() # Check if the tunnel is active and if not create it logger.debug('Checking if task "{}" has a running tunnel'.format(task)) out = os_shell('ps -ef | grep ":{}:{}:{}" | grep -v grep'.format(task.tunnel_port, task.ip, task.port), capture=True) if out.exit_code == 0: logger.debug('Task "{}" has a running tunnel, using it'.format(task)) else: logger.debug('Task "{}" has no running tunnel, creating it'.format(task)) # Get user keys user_keys = KeyPair.objects.get(user=task.user, default=True) # Tunnel command if task.computing.type == 'remotehop': # Get computing params first_host = task.computing.get_conf_param('first_host') first_user = task.computing.get_conf_param('first_user') #second_host = task.computing.get_conf_param('second_host') #second_user = task.computing.get_conf_param('second_user') #setup_command = task.computing.get_conf_param('setup_command') #base_port = task.computing.get_conf_param('base_port') tunnel_command= 'ssh -4 -i {} -o StrictHostKeyChecking=no -nNT -L 0.0.0.0:{}:{}:{} {}@{} & '.format(user_keys.private_key_file, task.tunnel_port, task.ip, task.port, first_user, first_host) else: tunnel_command= 'ssh -4 -o StrictHostKeyChecking=no -nNT -L 0.0.0.0:{}:{}:{} localhost & '.format(task.tunnel_port, task.ip, task.port) background_tunnel_command = 'nohup {} >/dev/null 2>&1 &'.format(tunnel_command) # Log logger.debug('Opening tunnel with command: {}'.format(background_tunnel_command)) # Execute subprocess.Popen(background_tunnel_command, shell=True) # Ok, now redirect to the task through the tunnel # Then, redirect to the task through the tunnel tunnel_host = get_tunnel_host() return redirect('http://{}:{}'.format(tunnel_host,task.tunnel_port)) Loading Loading @@ -965,15 +910,22 @@ def edit_computing_conf(request): #========================= # Sharable link handler #========================= @public_view def sharable_link_handler(request, id): # Get the task task = Task.objects.get(uuid__startswith=id) # First ensure that the tunnel is setu up setup_tunnel(task) # Then, redirect to the task through the tunnel tunnel_host = get_tunnel_host() return redirect('http://{}:{}'.format(tunnel_host,task.tunnel_port)) Loading
services/webapp/code/rosetta/urls.py +3 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,9 @@ urlpatterns = [ url(r'^containers/$', core_app_views.containers), url(r'^add_container/$', core_app_views.add_container), # Sharable link for tasks url(r'^t/(?P<id>\w{0,36})/$', core_app_views.sharable_link_handler), # Modules path('admin/', admin.site.urls), path('api/v1/doc/', get_swagger_view(title="Swagger Documentation")), Loading