From 9e551c06bf5f16f7cdb80917e74681c6c33fd3b0 Mon Sep 17 00:00:00 2001 From: Stefano Alberto Russo <stefano.russo@gmail.com> Date: Wed, 13 May 2020 13:17:33 +0200 Subject: [PATCH] Imporved and fixed bug in task creation. Minor other fixes. --- services/webapp/code/rosetta/core_app/api.py | 3 +- .../rosetta/core_app/computing_managers.py | 4 +- .../webapp/code/rosetta/core_app/models.py | 9 +- .../templates/components/container.html | 2 +- .../core_app/templates/components/task.html | 28 ++- .../core_app/templates/create_task.html | 64 +++++-- .../webapp/code/rosetta/core_app/views.py | 161 +++++++++--------- 7 files changed, 161 insertions(+), 110 deletions(-) diff --git a/services/webapp/code/rosetta/core_app/api.py b/services/webapp/code/rosetta/core_app/api.py index 87aa59d..a16731c 100644 --- a/services/webapp/code/rosetta/core_app/api.py +++ b/services/webapp/code/rosetta/core_app/api.py @@ -307,7 +307,8 @@ print(port) logger.info('Setting task "{}" to ip "{}" and port "{}"'.format(task.uuid, task_ip, task_port)) task.status = TaskStatuses.running task.ip = task_ip - task.port = int(task_port) + if task.container.supports_dynamic_ports: + task.port = int(task_port) task.save() # Notify the user that the task called back home diff --git a/services/webapp/code/rosetta/core_app/computing_managers.py b/services/webapp/code/rosetta/core_app/computing_managers.py index 22134d4..6efb0ad 100644 --- a/services/webapp/code/rosetta/core_app/computing_managers.py +++ b/services/webapp/code/rosetta/core_app/computing_managers.py @@ -317,8 +317,8 @@ class SlurmComputingManager(ComputingManager): # Submit the job if task.container.type == 'singularity': - if not task.container.supports_dynamic_ports: - raise Exception('This task does not support dynamic port allocation and is therefore not supported using singularity on Slurm') + #if not task.container.supports_dynamic_ports: + # raise Exception('This task does not support dynamic port allocation and is therefore not supported using singularity on Slurm') # Set pass if any if task.auth_pass: diff --git a/services/webapp/code/rosetta/core_app/models.py b/services/webapp/code/rosetta/core_app/models.py index 0a8b110..0af86c0 100644 --- a/services/webapp/code/rosetta/core_app/models.py +++ b/services/webapp/code/rosetta/core_app/models.py @@ -80,7 +80,7 @@ class Container(models.Model): image = models.CharField('Container image', max_length=255, blank=False, null=False) type = models.CharField('Container type', max_length=36, blank=False, null=False) registry = models.CharField('Container registry', max_length=255, blank=False, null=False) - ports = models.CharField('Container service ports', max_length=36, blank=True, null=True) + ports = models.CharField('Container ports', max_length=36, blank=True, null=True) # Capabilities supports_dynamic_ports = models.BooleanField(default=False) @@ -106,6 +106,13 @@ class Container(models.Model): color_map_index = string_int_hash % len(color_map) return color_map[color_map_index] + @property + def port(self): + if not self.ports: + return None + return(int(self.ports.split(',')[0])) + + #========================= diff --git a/services/webapp/code/rosetta/core_app/templates/components/container.html b/services/webapp/code/rosetta/core_app/templates/components/container.html index ec9c7f6..4422753 100644 --- a/services/webapp/code/rosetta/core_app/templates/components/container.html +++ b/services/webapp/code/rosetta/core_app/templates/components/container.html @@ -105,7 +105,7 @@ </div> <div style="margin-bottom:10px; text-align:center"> - <a href="/create_task?container_uuid={{ container.uuid }}" class="btn btn-light" style="border: #c0c0c0 1px solid"> <i class="fa fa-play" style="color:green"></i></a> + <a href="/create_task?task_container_uuid={{ container.uuid }}" class="btn btn-light" style="border: #c0c0c0 1px solid"> <i class="fa fa-play" style="color:green"></i></a> </div> diff --git a/services/webapp/code/rosetta/core_app/templates/components/task.html b/services/webapp/code/rosetta/core_app/templates/components/task.html index 73d3970..7447087 100644 --- a/services/webapp/code/rosetta/core_app/templates/components/task.html +++ b/services/webapp/code/rosetta/core_app/templates/components/task.html @@ -53,7 +53,7 @@ <tr> <td><b>IP</b></td> - <td>{{ task.ip}}</td> + <td>{{ task.ip }}</td> </tr> <tr> @@ -83,16 +83,23 @@ {% else %} <!-- <a href=?uuid={{task.uuid}}&action=start>Start</a> | --> <font color="#c0c0c0">Stop</font> | - {% endif %} <a href="?uuid={{task.uuid}}&action=delete&details=False">Delete</a> + + {% if task.port %} {% if task.status == "running" %} | <a href="?uuid={{task.uuid}}&action=connect">Connect</a> {% else %} | <font color="#c0c0c0">Connect</font> {% endif%} - - | <a href="/task_log/?uuid={{task.uuid}}&action=viewlog">View Log</a> + {% endif %} + + {% if task.status == "created" %} + | <font color="#c0c0c0">View Log</font> + {% else %} + | <a href="/task_log/?uuid={{task.uuid}}&action=viewlog">View Log</a> + {% endif %} + </td> @@ -144,23 +151,28 @@ <div style="margin-bottom:10px; text-align:center; padding:5px"> - <!-- Stop / Delete --> - {% if task.status == "stopped" %} + <!-- Stop / Delete / Cancel --> + {% if task.status == "stopped" or task.status == "created" %} <a href="?uuid={{task.uuid}}&action=delete&fromlist=True" class="btn btn-action">Delete</a> - {% else %} + {% else %} <a href="?uuid={{task.uuid}}&action=stop&fromlist=True" class="btn btn-action">Stop</a> {% endif %} <!-- Connect --> + {% if task.port %} {% if task.status == "running" %} <a href="?uuid={{task.uuid}}&action=connect" class="btn btn-connect">Connect</a> {% else %} <a href="" class="btn btn-disabled">Connect</a> {% endif %} + {% endif %} <!-- View log --> + {% if task.status == "created" %} + <a href="" class="btn btn-disabled">Logs</a> + {% else %} <a href="/task_log/?uuid={{task.uuid}}&action=viewlog" class="btn btn-action">Logs</a> - + {% endif %} </div> diff --git a/services/webapp/code/rosetta/core_app/templates/create_task.html b/services/webapp/code/rosetta/core_app/templates/create_task.html index ead1b8d..533fff8 100644 --- a/services/webapp/code/rosetta/core_app/templates/create_task.html +++ b/services/webapp/code/rosetta/core_app/templates/create_task.html @@ -23,7 +23,7 @@ <table class="dashboard" style="max-width:700px"> - <tr><td></td></tr> + <tr><td colspan="2"></td></tr> <tr> <td><b>Task name </b></td> @@ -34,9 +34,9 @@ <tr> <td><b>Task container</b></td><td> - {% if data.container %} + {% if data.task_container %} <select name="task_container_uuid"> - <option value="{{data.container.uuid}}" selected>{{data.container.name}} ({{data.container.type.title}})</option> + <option value="{{data.task_container.uuid}}" selected>{{data.task_container.name}} ({{data.task_container.type.title}})</option> </select> {% else %} <select name="task_container_uuid" > @@ -45,18 +45,16 @@ {% endfor %} </select> {% endif %} - <!-- | <a href="/add_container">Add new...</a> --> </td> </tr> <tr> <td><b>Computing resource</b></td><td> - <select name="task_computing" > + <select name="task_computing_uuid" > {% for computing in data.computings %}} - <option value="{{ computing.uuid }}">{{ computing.name}}</option> <!-- ({% if computing.user %}{{ computing.user }}{% else %}platform{% endif %}) --> + <option value="{{ computing.uuid }}">{{ computing.name}}</option> {% endfor %} </select> - <!-- | <a href="/add_computing">Add new...</a>--> </td> </tr> @@ -70,30 +68,60 @@ {% elif data.step == 'two' %} - <h3>Step 2: authentication and computing details</h3> + <h3>Step 2: add authentication and computing details</h3> <br/> + <table class="dashboard" style="max-width:700px"> + <tr><td colspan="2"></td></tr> + + <tr> + <td><b>Task name </b></td> + <td> + <input type="text" name="task_name_RECAP" value="{{ data.task_name }}" placeholder="" size="23" disabled /> + </td> + </tr> + + <tr> + <td><b>Task container</b></td><td> + <select name="task_container_uuid_RECAP"> + <option value="" selected>{{data.task_container.name}} ({{data.task_container.type.title}})</option> + </select> + </td> + </tr> + + <tr> + <td><b>Computing resource</b></td><td> + <select name="task_computing_uuid_RECAP" > + <option value="">{{ data.task_computing.name}}</option> + </select> + </td> + </tr> + + </table> - {% if data.task.container.type == 'singularity' and not data.task.container.supports_dynamic_ports %} - <div> <p style="font-size:15px; max-width:700px; margin-bottom:20px"> + <br> + {% if data.task_container.type == 'singularity' and not data.task_container.supports_dynamic_ports %} + <div> <p style="font-size:15px; max-width:700px; margin-bottom:20px; margin-left:5px"> <i class="fa fa-exclamation-triangle" style="color:orange"></i> This container does not support dynamic ports and you are running it with Singularity, without network insulation. This means that if the container port is already occupied, it will not be able to start. </p></div> {% endif %} - {% if data.task.container.ports and not data.task.container.supports_pass_auth %} - <div> <p style="font-size:15px; max-width:700px; margin-bottom:20px"> - <i class="fa fa-exclamation-triangle" style="color:orange"></i> This container does not support any authentication. This means that anyone running on the same network will be able to access it services. + {% if data.task_container.ports and not data.task_container.supports_pass_auth %} + <div> <p style="font-size:15px; max-width:700px; margin-bottom:20px; margin-left:5px"> + <i class="fa fa-exclamation-triangle" style="color:orange"></i> This container does not support configuring any authentication. This means that unless it is built-in within the container, anyone running on the same network will be able to access it. </p></div> {% endif %} <form action="/create_task/" method="POST"> {% csrf_token %} <input type="hidden" name="step" value="two" /> - <input type="hidden" name="task_uuid" value="{{ data.task.uuid }}" /> + <input type="hidden" name="task_name" value="{{ data.task_name }}" /> + <input type="hidden" name="task_container_uuid" value="{{ data.task_container.uuid }}" /> + <input type="hidden" name="task_computing_uuid" value="{{ data.task_computing.uuid }}" /> <table class="dashboard" style="max-width:700px"> - <tr><td></td></tr> - {% if data.task.container.supports_user_auth %} + <tr><td colspan="2"></td></tr> + {% if data.task_container.supports_user_auth %} <tr> <td><b>Task user</b></td> <td> @@ -102,7 +130,7 @@ </tr> {% endif %} - {% if data.task.container.supports_pass_auth %} + {% if data.task_container.supports_pass_auth %} <tr> <td valign="top"><b>Set task password</b></td> <td> @@ -131,7 +159,7 @@ </td> </tr> --> - {% if data.task.computing.type == 'slurm' %} + {% if data.task_computing.type == 'slurm' %} <tr> <td><b>Computing options</b></td> <td> diff --git a/services/webapp/code/rosetta/core_app/views.py b/services/webapp/code/rosetta/core_app/views.py index 9d6a91f..c80aba8 100644 --- a/services/webapp/code/rosetta/core_app/views.py +++ b/services/webapp/code/rosetta/core_app/views.py @@ -466,12 +466,12 @@ def create_task(request): step = request.POST.get('step', None) # Container uuid if any - container_uuid = request.GET.get('container_uuid', None) + container_uuid = request.GET.get('task_container_uuid', None) if container_uuid: try: - data['container'] = Container.objects.get(uuid=container_uuid, user=request.user) + data['task_container'] = Container.objects.get(uuid=container_uuid, user=request.user) except Container.DoesNotExist: - data['container'] = Container.objects.get(uuid=container_uuid, user=None) + data['task_container'] = Container.objects.get(uuid=container_uuid, user=None) else: # Get containers data['containers'] = list(Container.objects.filter(user=None)) + list(Container.objects.filter(user=request.user)) @@ -479,12 +479,15 @@ def create_task(request): # Get computings data['computings'] = list(Computing.objects.filter(user=None)) + list(Computing.objects.filter(user=request.user)) - - if step == 'one': + # Handle step + if step: - # We have a step one submitted, get the first tab parameters + # Task name task_name = request.POST.get('task_name', None) + if not task_name: + raise ErrorMessage('Missing task name') + data['task_name'] = task_name # Task container task_container_uuid = request.POST.get('task_container_uuid', None) @@ -495,9 +498,10 @@ def create_task(request): task_container = Container.objects.get(uuid=task_container_uuid, user=request.user) except Container.DoesNotExist: raise Exception('Consistency error, container with uuid "{}" does not exists or user "{}" does not have access rights'.format(task_container_uuid, request.user.email)) + data['task_container'] = task_container - # task computing - task_computing_uuid = request.POST.get('task_computing', None) + # Task computing + task_computing_uuid = request.POST.get('task_computing_uuid', None) try: task_computing = Computing.objects.get(uuid=task_computing_uuid, user=None) except Computing.DoesNotExist: @@ -505,87 +509,86 @@ def create_task(request): task_computing = Computing.objects.get(uuid=task_computing_uuid, user=request.user) except Computing.DoesNotExist: raise Exception('Consistency error, computing with uuid "{}" does not exists or user "{}" does not have access rights'.format(task_computing_uuid, request.user.email)) + data['task_computing'] = task_computing + + # Handle step one/two + if step == 'one': + + # Set step and task uuid + data['step'] = 'two' + + elif step == 'two': + + # Generate the task uuid + task_uuid = str(uuid.uuid4()) + + # Create the task object + task = Task(uuid = task_uuid, + user = request.user, + name = task_name, + status = TaskStatuses.created, + container = task_container, + computing = task_computing) + + # Add auth + task.auth_user = request.POST.get('auth_user', None) + task.auth_pass = request.POST.get('auth_password', None) + task.access_method = request.POST.get('access_method', None) + + # Cheks + if task.auth_pass and len(task.auth_pass) < 6: + raise ErrorMessage('Task password must be at least 6 chars') + + # Computing options # TODO: This is hardcoded thinking about Slurm + computing_cpus = request.POST.get('computing_cpus', None) + computing_memory = request.POST.get('computing_memory', None) + computing_partition = request.POST.get('computing_partition', None) + + computing_options = {} + if computing_cpus: + try: + int(computing_cpus) + except: + raise Exception('Cannot convert computing_cpus to int') + computing_options['cpus'] = int(computing_cpus) + + if computing_memory: + computing_options['memory'] = computing_memory + + if computing_partition: + computing_options['partition'] = computing_partition + + if computing_options: + task.computing_options = computing_options + + # Attach user config to computing + task.computing.attach_user_conf_data(task.user) + # Set port if not dynamic ports + if not task.container.supports_dynamic_ports: + if task.container.ports: + task.port = task.container.port - # Generate the task uuid - task_uuid = str(uuid.uuid4()) - - # Create the task object - task = Task(uuid = task_uuid, - user = request.user, - name = task_name, - status = TaskStatuses.created, - container = task_container, - computing = task_computing) - - # Save the task in the cache - _task_cache[task_uuid] = task - - # Set step and task uuid - data['step'] = 'two' - data['task'] = task - - elif step == 'two': - - # Get back the task - task_uuid = request.POST.get('task_uuid', None) - task = _task_cache[task_uuid] - - # Add auth - task.auth_user = request.POST.get('auth_user', None) - task.auth_pass = request.POST.get('auth_password', None) - task.access_method = request.POST.get('access_method', None) - - # Cheks - if task.auth_pass and len(task.auth_pass) < 6: - raise ErrorMessage('Task password must be at least 6 chars') - - # Computing options # TODO: This is hardcoded thinking about Slurm - computing_cpus = request.POST.get('computing_cpus', None) - computing_memory = request.POST.get('computing_memory', None) - computing_partition = request.POST.get('computing_partition', None) - - computing_options = {} - if computing_cpus: + # Save the task before starting it, or the computing manager will not be able to work properly + task.save() + + # Start the task try: - int(computing_cpus) + task.computing.manager.start_task(task) except: - raise Exception('Cannot convert computing_cpus to int') - computing_options['cpus'] = int(computing_cpus) - - if computing_memory: - computing_options['memory'] = computing_memory - - if computing_partition: - computing_options['partition'] = computing_partition - - if computing_options: - task.computing_options = computing_options - - logger.debug('computing_options="{}"'.format(computing_options)) - - # Save the task in the DB - task.save() - - # Attach user config to computing - task.computing.attach_user_conf_data(task.user) - - # Start the task - #try: - task.computing.manager.start_task(task) - #except: - # task.delete() - # raise + # Delete the task if could not start it + task.delete() + + # ..and re-raise + raise - # Set step - data['step'] = 'created' + # Set step + data['step'] = 'created' else: # Set step data['step'] = 'one' - - return render(request, 'create_task.html', {'data': data}) -- GitLab