Commit 059d6cd7 authored by Stefano Alberto Russo's avatar Stefano Alberto Russo
Browse files

Added the task direct link, and decoupled the tunnel creation part from the task view.

parent 93f32d0f
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -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)



#=========================
+11 −4
Original line number Diff line number Diff line
@@ -80,10 +80,8 @@
          </div>



          {% if data.task %}      

         
          <div style="float:left; max-width:450px">
          <table class="dashboard" style="margin:10px">

            <!-- <tr>
@@ -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>
            
@@ -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>



+68 −0
Original line number Diff line number Diff line
@@ -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)





+18 −66
Original line number Diff line number Diff line
@@ -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

@@ -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))

@@ -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))
    


+3 −0
Original line number Diff line number Diff line
@@ -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")),