From 9d33e22a32b3d564dfe65c9bf0e7b45f01801c44 Mon Sep 17 00:00:00 2001 From: Stefano Alberto Russo <stefano.russo@gmail.com> Date: Thu, 30 Apr 2020 17:32:25 +0200 Subject: [PATCH] Fixes. --- containers/MetaDesktop/files/entrypoint.sh | 6 +++++ containers/MetaDesktop/files/run_novnc.sh | 8 +++---- containers/MetaDesktop/files/run_vnc.sh | 10 +++++++-- services/slurmbase/Dockerfile | 5 +++++ services/webapp/code/rosetta/base_app/api.py | 12 +++++----- .../rosetta/base_app/computing_managers.py | 17 ++++++++------ .../management/commands/base_app_populate.py | 22 +++++++++++-------- .../webapp/code/rosetta/base_app/models.py | 6 ++--- .../templates/components/container.html | 2 +- .../webapp/code/rosetta/base_app/views.py | 12 +++++----- 10 files changed, 63 insertions(+), 37 deletions(-) diff --git a/containers/MetaDesktop/files/entrypoint.sh b/containers/MetaDesktop/files/entrypoint.sh index f818af5..9fa82fa 100644 --- a/containers/MetaDesktop/files/entrypoint.sh +++ b/containers/MetaDesktop/files/entrypoint.sh @@ -7,6 +7,12 @@ set -e echo "" echo "[INFO] Executing entrypoint..." +if [ "x$BASE_PORT" == "x" ]; then + echo "[INFO] No task base port set, will set noVNC port 8590 and VNC port 5900 with desktop id \"0\"" +else + echo "[INFO] Task base port set, will set noVNC port $BASE_PORT and noVNC port $(($BASE_PORT+1)) with desktop id \"$(($BASE_PORT-5900+1))\"" +fi + #--------------------- # Setup home #--------------------- diff --git a/containers/MetaDesktop/files/run_novnc.sh b/containers/MetaDesktop/files/run_novnc.sh index 3caeaf8..5319878 100755 --- a/containers/MetaDesktop/files/run_novnc.sh +++ b/containers/MetaDesktop/files/run_novnc.sh @@ -2,11 +2,11 @@ # Exec TigerVNC server -if [ "x$TASK_PORT" == "x" ]; then +if [ "x$BASE_PORT" == "x" ]; then /usr/lib/noVNC/utils/launch.sh --listen 8590 - echo "Running noVN on port 8590" + echo "Running noVNC on port 8590" else - /usr/lib/noVNC/utils/launch.sh --listen $TASK_PORT - echo "Running noVN on port $TASK_PORT" + /usr/lib/noVNC/utils/launch.sh --listen $BASE_PORT --vnc localhost:$(($BASE_PORT+1)) + echo "Running noVNC on port $BASE_PORT and connecting to VNC on port $(($BASE_PORT+1))" fi diff --git a/containers/MetaDesktop/files/run_vnc.sh b/containers/MetaDesktop/files/run_vnc.sh index d42a222..ac7daeb 100755 --- a/containers/MetaDesktop/files/run_vnc.sh +++ b/containers/MetaDesktop/files/run_vnc.sh @@ -2,10 +2,16 @@ # Exec TigerVNC server +if [ "x$BASE_PORT" == "x" ]; then + DESKTOP_NUMBER=0 +else + DESKTOP_NUMBER=$(($BASE_PORT-5900+1)) +fi + if [ "x$VNC_AUTH" == "xTrue" ]; then - /opt/tigervnc/usr/bin/vncserver :0 -SecurityTypes vncauth,tlsvnc -xstartup /opt/tigervnc/xstartup + /opt/tigervnc/usr/bin/vncserver :$DESKTOP_NUMBER -SecurityTypes vncauth,tlsvnc -xstartup /opt/tigervnc/xstartup else - /opt/tigervnc/usr/bin/vncserver :0 -SecurityTypes None -xstartup /opt/tigervnc/xstartup + /opt/tigervnc/usr/bin/vncserver :$DESKTOP_NUMBER -SecurityTypes None -xstartup /opt/tigervnc/xstartup fi diff --git a/services/slurmbase/Dockerfile b/services/slurmbase/Dockerfile index 8297d22..7abc481 100755 --- a/services/slurmbase/Dockerfile +++ b/services/slurmbase/Dockerfile @@ -22,3 +22,8 @@ COPY slurm.conf /etc/slurm-llnl/slurm.conf # TODO: why do we need this? RUN ln -s /var/lib/slurm-llnl /var/lib/slurm-wlm RUN ln -s /var/log/slurm-llnl /var/log/slurm-wlm + +# Add slurmtestuser user +RUN useradd slurmtestuser +RUN cp -a /rosetta/.ssh /home/slurmtestuser +RUN chown -R slurmtestuser:slurmtestuser /home/slurmtestuser diff --git a/services/webapp/code/rosetta/base_app/api.py b/services/webapp/code/rosetta/base_app/api.py index bcc9f32..6bbcb2c 100644 --- a/services/webapp/code/rosetta/base_app/api.py +++ b/services/webapp/code/rosetta/base_app/api.py @@ -260,18 +260,20 @@ from random import randint while True: # Get a random ephimeral port - port = randint(49152, 65535) + port = randint(49152, 65535-2) # Check port is available sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - result = sock.connect_ex(('127.0.0.1', port)) - if result == 0: - print('Found not available ephimeral port ({}) , choosing another one...'.format(port)) + result1 = sock.connect_ex(('127.0.0.1', port)) + result2 = sock.connect_ex(('127.0.0.1', port+1)) + result3 = sock.connect_ex(('127.0.0.1', port+2)) + if (result1 == 0) or (result2 == 0) or (result3 == 0): + logger.info('Found not available ephemeral port triplet ({},{},{}) , choosing another one...'.format(port,port+1,port+2)) import time time.sleep(1) else: break -logger.info(' - port: "{}"'.format(port)) +logger.info(' - ports: "{},{},{}"'.format(port, port+1, port+2)) response = urlopen("'''+host_conn_string+'''/api/v1/base/agent/?task_uuid={}&action=set_ip_port&ip={}&port={}".format(task_uuid, ip, port)) response_content = response.read() diff --git a/services/webapp/code/rosetta/base_app/computing_managers.py b/services/webapp/code/rosetta/base_app/computing_managers.py index dc797c2..3ad3aaa 100644 --- a/services/webapp/code/rosetta/base_app/computing_managers.py +++ b/services/webapp/code/rosetta/base_app/computing_managers.py @@ -116,7 +116,7 @@ class LocalComputingManager(ComputingManager): task.tid = task_tid task.status = TaskStatuses.running task.ip = task_ip - task.port = int(task.container.service_ports.split(',')[0]) + task.port = int(task.container.default_ports.split(',')[0]) # Save task.save() @@ -179,13 +179,13 @@ class RemoteComputingManager(ComputingManager): webapp_ip = socket.gethostbyname(hostname) run_command = 'ssh -i {} -4 -o StrictHostKeyChecking=no {} '.format(user_keys.private_key_file, host) - run_command+= '"wget {}:8080/api/v1/base/agent/?task_uuid={} -O /tmp/agent_{}.py &> /dev/null && export TASK_PORT=\$(python /tmp/agent_{}.py 2> /tmp/{}.log) && '.format(webapp_ip, task.uuid, task.uuid, task.uuid, task.uuid) - run_command += 'export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_TASK_PORT=\$TASK_PORT && {} '.format(authstring) + run_command+= '"wget {}:8080/api/v1/base/agent/?task_uuid={} -O /tmp/agent_{}.py &> /dev/null && export BASE_PORT=\$(python /tmp/agent_{}.py 2> /tmp/{}.log) && '.format(webapp_ip, task.uuid, task.uuid, task.uuid, task.uuid) + run_command += 'export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_BASE_PORT=\$BASE_PORT && {} '.format(authstring) run_command += 'exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv ' # ssh -i /rosetta/.ssh/id_rsa -4 -o StrictHostKeyChecking=no slurmclusterworker-one # "wget 172.21.0.2:8080/api/v1/base/agent/?task_uuid=15a4320a-88b6-4ffc-8dd0-c80f9d18b292 -O /tmp/agent_15a4320a-88b6-4ffc-8dd0-c80f9d18b292.py &> /dev/null && - # export TASK_PORT=\$(python /tmp/agent_15a4320a-88b6-4ffc-8dd0-c80f9d18b292.py) && export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_TASK_PORT=\$TASK_PORT && export SINGULARITYENV_AUTH_PASS=testpass && + # export BASE_PORT=\$(python /tmp/agent_15a4320a-88b6-4ffc-8dd0-c80f9d18b292.py) && export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_BASE_PORT=\$BASE_PORT && export SINGULARITYENV_AUTH_PASS=testpass && # exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv # docker://dregistry:5000/rosetta/metadesktop &> /tmp/15a4320a-88b6-4ffc-8dd0-c80f9d18b292.log & echo $!" @@ -286,6 +286,9 @@ class SlurmComputingManager(ComputingManager): if task.container.type == 'singularity': + if not task.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: authstring = ' export SINGULARITYENV_AUTH_PASS={} && '.format(task.auth_pass) @@ -298,15 +301,15 @@ class SlurmComputingManager(ComputingManager): run_command = 'ssh -i {} -4 -o StrictHostKeyChecking=no {} '.format(user_keys.private_key_file, host) - run_command += '"echo \\"#!/bin/bash\nwget {}:8080/api/v1/base/agent/?task_uuid={} -O /tmp/agent_{}.py &> /dev/null && export TASK_PORT=\\\\\\$(python /tmp/agent_{}.py 2> /tmp/{}.log) && '.format(webapp_ip, task.uuid, task.uuid, task.uuid, task.uuid) - run_command += 'export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_TASK_PORT=\\\\\\$TASK_PORT && {} '.format(authstring) + run_command += '"echo \\"#!/bin/bash\nwget {}:8080/api/v1/base/agent/?task_uuid={} -O /tmp/agent_{}.py &> /dev/null && export BASE_PORT=\\\\\\$(python /tmp/agent_{}.py 2> /tmp/{}.log) && '.format(webapp_ip, task.uuid, task.uuid, task.uuid, task.uuid) + run_command += 'export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_BASE_PORT=\\\\\\$BASE_PORT && {} '.format(authstring) run_command += 'exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv ' # Double to escape for python six for shell (double times three as \\\ escapes a single slash in shell) # ssh -i /rosetta/.ssh/id_rsa -4 -o StrictHostKeyChecking=no slurmclustermaster-main "echo \"wget 172.18.0.5:8080/api/v1/base/agent/?task_uuid=558c65c3-8b72-4d6b-8119-e1dcf6f81177 -O /tmp/agent_558c65c3-8b72-4d6b-8119-e1dcf6f81177.py &> /dev/null - # && export TASK_PORT=\\\$(python /tmp/agent_558c65c3-8b72-4d6b-8119-e1dcf6f81177.py 2> /tmp/558c65c3-8b72-4d6b-8119-e1dcf6f81177.log) && export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_TASK_PORT=\\\$TASK_PORT && export SINGULARITYENV_AUTH_PASS=testpass + # && export BASE_PORT=\\\$(python /tmp/agent_558c65c3-8b72-4d6b-8119-e1dcf6f81177.py 2> /tmp/558c65c3-8b72-4d6b-8119-e1dcf6f81177.log) && export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_BASE_PORT=\\\$BASE_PORT && export SINGULARITYENV_AUTH_PASS=testpass # && exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv docker://dregistry:5000/rosetta/metadesktop &> /tmp/558c65c3-8b72-4d6b-8119-e1dcf6f81177.log\" > /tmp/558c65c3-8b72-4d6b-8119-e1dcf6f81177.sh" diff --git a/services/webapp/code/rosetta/base_app/management/commands/base_app_populate.py b/services/webapp/code/rosetta/base_app/management/commands/base_app_populate.py index bad2e58..28ce4ac 100644 --- a/services/webapp/code/rosetta/base_app/management/commands/base_app_populate.py +++ b/services/webapp/code/rosetta/base_app/management/commands/base_app_populate.py @@ -53,7 +53,8 @@ class Command(BaseCommand): image = 'rosetta/metadesktop', type = 'docker', registry = 'docker_local', - service_ports = '8590', + default_ports = '8590', + dynamic_ports = True, require_pass = True) # MetaDesktop Singularity @@ -62,7 +63,8 @@ class Command(BaseCommand): image = 'rosetta/metadesktop', type = 'singularity', registry = 'docker_local', - service_ports = '8590', + default_ports = '8590', + dynamic_ports = True, require_pass = True) # Astrocook @@ -71,15 +73,16 @@ class Command(BaseCommand): image = 'sarusso/astrocook:b2b819e', type = 'docker', registry = 'docker_local', - service_ports = '8590') + dynamic_ports = False, + default_ports = '8590') - # Public containers + # Private containers testuser_containers = Container.objects.filter(user=testuser) if testuser_containers: - print('Not creating testuser containers as they already exist') + print('Not creating testuser private containers as they already exist') else: - print('Creating testuser containers...') + print('Creating testuser private containers...') # JuPyter Container.objects.create(user = testuser, @@ -87,7 +90,8 @@ class Command(BaseCommand): image = 'jupyter/base-notebook', type = 'docker', registry = 'docker_hub', - service_ports = '8888') + dynamic_ports = False, + default_ports = '8888') # Computing resources computing_resources = Computing.objects.all() @@ -122,7 +126,7 @@ class Command(BaseCommand): ComputingUserConf.objects.create(user = testuser, computing = demo_remote_auth_computing, - data = {'user': 'testuser'}) + data = {'user': 'slurmtestuser'}) #============================== @@ -142,6 +146,6 @@ class Command(BaseCommand): # Create demo slurm user computing conf ComputingUserConf.objects.create(user = testuser, computing = demo_slurm_computing, - data = {'user': 'testuser'}) + data = {'user': 'slurmtestuser'}) diff --git a/services/webapp/code/rosetta/base_app/models.py b/services/webapp/code/rosetta/base_app/models.py index 57c0c5d..2110a1a 100644 --- a/services/webapp/code/rosetta/base_app/models.py +++ b/services/webapp/code/rosetta/base_app/models.py @@ -80,14 +80,14 @@ 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) - service_ports = models.CharField('Container service ports', max_length=36, blank=True, null=True) - #private = models.BooleanField('Container is private and needs auth to be pulled from the registry') + default_ports = models.CharField('Container service ports', max_length=36, blank=True, null=True) + dynamic_ports = models.BooleanField(default=False) require_user = models.BooleanField(default=False) require_pass = models.BooleanField(default=False) def __str__(self): - return str('Container of type "{}" with image "{}" with service ports "{}" from registry "{}" of user "{}"'.format(self.type, self.image, self.service_ports, self.registry, self.user)) + return str('Container of type "{}" with image "{}" with service ports "{}" from registry "{}" of user "{}"'.format(self.type, self.image, self.default_ports, self.registry, self.user)) @property diff --git a/services/webapp/code/rosetta/base_app/templates/components/container.html b/services/webapp/code/rosetta/base_app/templates/components/container.html index e4c1edb..41486ee 100644 --- a/services/webapp/code/rosetta/base_app/templates/components/container.html +++ b/services/webapp/code/rosetta/base_app/templates/components/container.html @@ -32,7 +32,7 @@ <tr> <td><b>Service port(s)</b></td> - <td>{{ container.service_ports}}</td> + <td>{{ container.default_ports}}</td> </tr> {% if container.user %} diff --git a/services/webapp/code/rosetta/base_app/views.py b/services/webapp/code/rosetta/base_app/views.py index 341a45d..95cb157 100644 --- a/services/webapp/code/rosetta/base_app/views.py +++ b/services/webapp/code/rosetta/base_app/views.py @@ -621,17 +621,17 @@ def add_container(request): container_name = request.POST.get('container_name', None) # Container service ports. TODO: support multiple ports? - container_service_ports = request.POST.get('container_service_ports', None) + container_default_ports = request.POST.get('container_default_ports', None) - if container_service_ports: + if container_default_ports: try: - for container_service_port in container_service_ports.split(','): + for container_service_port in container_default_ports.split(','): int(container_service_port) except: - raise ErrorMessage('Invalid service port(s) in "{}"'.format(container_service_ports)) + raise ErrorMessage('Invalid service port(s) in "{}"'.format(container_default_ports)) # Log - logger.debug('Creating new container object with image="{}", type="{}", registry="{}", service_ports="{}"'.format(container_image, container_type, container_registry, container_service_ports)) + logger.debug('Creating new container object with image="{}", type="{}", registry="{}", default_ports="{}"'.format(container_image, container_type, container_registry, container_default_ports)) # Create Container.objects.create(user = request.user, @@ -639,7 +639,7 @@ def add_container(request): name = container_name, type = container_type, registry = container_registry, - service_ports = container_service_ports) + default_ports = container_default_ports) # Set added switch data['added'] = True -- GitLab