Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • develop
  • feature/arch_support
  • feature/global_refactoring
  • master
  • v1.0.0
5 results

Target

Select target project
  • exact/Rosetta
1 result
Select Git revision
  • develop
  • feature/arch_support
  • feature/global_refactoring
  • master
  • v1.0.0
5 results
Show changes
Commits on Source (5)
Showing
with 630 additions and 528 deletions
...@@ -12,7 +12,7 @@ if [[ "x$1" == "xnocache" ]] ; then ...@@ -12,7 +12,7 @@ if [[ "x$1" == "xnocache" ]] ; then
NOCACHE=true NOCACHE=true
SERVICE="" SERVICE=""
elif [[ "x$2" == "xnocache" ]] ; then elif [[ "x$2" == "xnocache" ]] ; then
NOCACHE=true NOCACHE=true
SERVICE=$1 SERVICE=$1
else else
if [[ "x$NOCACHE" == "x" ]] ; then if [[ "x$NOCACHE" == "x" ]] ; then
...@@ -29,20 +29,20 @@ else ...@@ -29,20 +29,20 @@ else
fi fi
if [[ "x$SERVICE" == "x" ]] ; then if [[ "x$SERVICE" == "x" ]] ; then
# Build all services # Build all services
NOCACHE=$NOCACHE rosetta/build base_ubuntu18.04 NOCACHE=$NOCACHE rosetta/build base_ubuntu18.04
NOCACHE=$NOCACHE rosetta/build base_ubuntu22.04 NOCACHE=$NOCACHE rosetta/build base_ubuntu22.04
NOCACHE=$NOCACHE rosetta/build slurmbase NOCACHE=$NOCACHE rosetta/build slurmbase
NOCACHE=$NOCACHE rosetta/build slurmcluster NOCACHE=$NOCACHE rosetta/build slurmcluster
NOCACHE=$NOCACHE rosetta/build slurmclustermaster NOCACHE=$NOCACHE rosetta/build slurmclustermaster
NOCACHE=$NOCACHE rosetta/build slurmclusterworker NOCACHE=$NOCACHE rosetta/build slurmclusterworker
NOCACHE=$NOCACHE rosetta/build standaloneworker NOCACHE=$NOCACHE rosetta/build standaloneworker
NOCACHE=$NOCACHE rosetta/build dregistry NOCACHE=$NOCACHE rosetta/build dregistry
NOCACHE=$NOCACHE rosetta/build webapp NOCACHE=$NOCACHE rosetta/build webapp
NOCACHE=$NOCACHE rosetta/build postgres NOCACHE=$NOCACHE rosetta/build postgres
NOCACHE=$NOCACHE rosetta/build proxy NOCACHE=$NOCACHE rosetta/build proxy
else else
# Build a specific image # Build a specific image
......
...@@ -14,7 +14,7 @@ fi ...@@ -14,7 +14,7 @@ fi
if [[ "x$2" != "x" ]] ; then if [[ "x$2" != "x" ]] ; then
tail -f -n 1000 data/$1/log/$2.log tail -f -n 1000 data/$1/log/$2.log
else else
docker-compose logs -f $1 docker-compose logs -f $1
fi fi
...@@ -14,7 +14,7 @@ elif [[ $# -gt 2 ]] ; then ...@@ -14,7 +14,7 @@ elif [[ $# -gt 2 ]] ; then
echo "Use double quotes to wrap commands with spaces" echo "Use double quotes to wrap commands with spaces"
exit 1 exit 1
else else
COMMAND=$2 COMMAND=$2
if [[ "x$COMMAND" == "x" ]] ; then if [[ "x$COMMAND" == "x" ]] ; then
echo "" echo ""
......
...@@ -10,16 +10,16 @@ if [[ $# -eq 0 ]] ; then ...@@ -10,16 +10,16 @@ if [[ $# -eq 0 ]] ; then
declare -a container_names declare -a container_names
OUT=$(rosetta/ps) OUT=$(rosetta/ps)
while read -r line; do while read -r line; do
if [[ $line == *"Up"* ]]; then if [[ $line == *"Up"* ]]; then
container_name=$(echo $line | cut -d ' ' -f1) container_name=$(echo $line | cut -d ' ' -f1)
container_names+=($container_name); container_names+=($container_name);
fi fi
done <<< "$OUT" done <<< "$OUT"
for container_name in ${container_names[@]} for container_name in ${container_names[@]}
do do
echo "" echo ""
...@@ -27,7 +27,7 @@ if [[ $# -eq 0 ]] ; then ...@@ -27,7 +27,7 @@ if [[ $# -eq 0 ]] ; then
docker-compose exec $container_name /bin/bash -c "supervisorctl status" docker-compose exec $container_name /bin/bash -c "supervisorctl status"
done done
echo "" echo ""
else else
docker-compose exec $@ /bin/bash -c "supervisorctl status" docker-compose exec $@ /bin/bash -c "supervisorctl status"
fi fi
...@@ -9,9 +9,9 @@ logger = logging.getLogger(__name__) ...@@ -9,9 +9,9 @@ logger = logging.getLogger(__name__)
class RosettaOIDCAuthenticationBackend(OIDCAuthenticationBackend): class RosettaOIDCAuthenticationBackend(OIDCAuthenticationBackend):
def create_user(self, claims): def create_user(self, claims):
# Call parent user creation function # Call parent user creation function
user = super(RosettaOIDCAuthenticationBackend, self).create_user(claims) user = super(RosettaOIDCAuthenticationBackend, self).create_user(claims)
...@@ -27,12 +27,12 @@ class RosettaOIDCAuthenticationBackend(OIDCAuthenticationBackend): ...@@ -27,12 +27,12 @@ class RosettaOIDCAuthenticationBackend(OIDCAuthenticationBackend):
class RosettaOIDCAuthenticationCallbackView(OIDCAuthenticationCallbackView): class RosettaOIDCAuthenticationCallbackView(OIDCAuthenticationCallbackView):
def login_success(self): def login_success(self):
# Call parent login_success but do not return # Call parent login_success but do not return
super(RosettaOIDCAuthenticationCallbackView, self).login_success() super(RosettaOIDCAuthenticationCallbackView, self).login_success()
logger.debug('Trying to get cookie-based post login redirect') logger.debug('Trying to get cookie-based post login redirect')
post_login_page = self.request.COOKIES.get('post_login_redirect') post_login_page = self.request.COOKIES.get('post_login_redirect')
if post_login_page: if post_login_page:
...@@ -44,3 +44,4 @@ class RosettaOIDCAuthenticationCallbackView(OIDCAuthenticationCallbackView): ...@@ -44,3 +44,4 @@ class RosettaOIDCAuthenticationCallbackView(OIDCAuthenticationCallbackView):
logger.debug('No cookie-based post login redirect found, redirecting to "%s"', self.success_url) logger.debug('No cookie-based post login redirect found, redirecting to "%s"', self.success_url)
return HttpResponseRedirect(self.success_url) return HttpResponseRedirect(self.success_url)
...@@ -2,7 +2,7 @@ import os ...@@ -2,7 +2,7 @@ import os
from django.conf import settings from django.conf import settings
def export_vars(request): def export_vars(request):
data = {} data = {}
# Set open id connect enabled or not # Set open id connect enabled or not
if settings.OIDC_RP_CLIENT_ID: if settings.OIDC_RP_CLIENT_ID:
data['OPENID_ENABLED'] = True data['OPENID_ENABLED'] = True
...@@ -19,6 +19,6 @@ def export_vars(request): ...@@ -19,6 +19,6 @@ def export_vars(request):
if settings.INVITATION_CODE: if settings.INVITATION_CODE:
data['INVITATION_CODE_ENABLED'] = True data['INVITATION_CODE_ENABLED'] = True
else: else:
data['INVITATION_CODE_ENABLED'] = False data['INVITATION_CODE_ENABLED'] = False
return data return data
\ No newline at end of file
...@@ -51,7 +51,7 @@ def public_view(wrapped_view): ...@@ -51,7 +51,7 @@ def public_view(wrapped_view):
error_text = str(e) error_text = str(e)
else: else:
# Log the exception # Log the exception
logger.error(format_exception(e)) logger.error(format_exception(e))
# Raise the exception if we are in debug mode # Raise the exception if we are in debug mode
...@@ -109,13 +109,13 @@ def private_view(wrapped_view): ...@@ -109,13 +109,13 @@ def private_view(wrapped_view):
error_text = str(e) error_text = str(e)
else: else:
# Log the exception # Log the exception
logger.error(format_exception(e)) logger.error(format_exception(e))
# Raise the exception if we are in debug mode # Raise the exception if we are in debug mode
if settings.DEBUG: if settings.DEBUG:
raise raise
# Otherwise, mask it # Otherwise, mask it
else: else:
error_text = 'something went wrong ({})'.format(e) error_text = 'something went wrong ({})'.format(e)
...@@ -137,3 +137,4 @@ def private_view(wrapped_view): ...@@ -137,3 +137,4 @@ def private_view(wrapped_view):
response.set_cookie('post_login_redirect', request.build_absolute_uri()) response.set_cookie('post_login_redirect', request.build_absolute_uri())
return response return response
return private_view_wrapper return private_view_wrapper
...@@ -3,4 +3,4 @@ class ErrorMessage(Exception): ...@@ -3,4 +3,4 @@ class ErrorMessage(Exception):
pass pass
class ConsistencyException(Exception): class ConsistencyException(Exception):
pass pass
\ No newline at end of file
...@@ -26,4 +26,4 @@ class JSONField(Field): ...@@ -26,4 +26,4 @@ class JSONField(Field):
def value_to_string(self, obj): def value_to_string(self, obj):
return self.value_from_object(obj) return self.value_from_object(obj)
# Credits: https://medium.com/@philamersune/using-postgresql-jsonfield-in-sqlite-95ad4ad2e5f1 # Credits: https://medium.com/@philamersune/using-postgresql-jsonfield-in-sqlite-95ad4ad2e5f1
\ No newline at end of file
...@@ -8,13 +8,13 @@ class Command(BaseCommand): ...@@ -8,13 +8,13 @@ class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
#===================== #=====================
# Testuser # Testuser
#===================== #=====================
try: try:
testuser = User.objects.get(username='testuser') testuser = User.objects.get(username='testuser')
print('Not creating test user as it already exists') print('Not creating test user as it already exists')
except User.DoesNotExist: except User.DoesNotExist:
print('Creating test user with default password') print('Creating test user with default password')
testuser = User.objects.create_user('testuser', 'testuser@rosetta.platform', 'testpass') testuser = User.objects.create_user('testuser', 'testuser@rosetta.platform', 'testpass')
...@@ -22,7 +22,7 @@ class Command(BaseCommand): ...@@ -22,7 +22,7 @@ class Command(BaseCommand):
testuser.is_staff = True testuser.is_staff = True
testuser.is_admin=True testuser.is_admin=True
testuser.is_superuser=True testuser.is_superuser=True
testuser.save() testuser.save()
print('Creating testuser profile') print('Creating testuser profile')
Profile.objects.create(user=testuser, auth='local', authtoken='129aac94-284a-4476-953c-ffa4349b4a50') Profile.objects.create(user=testuser, auth='local', authtoken='129aac94-284a-4476-953c-ffa4349b4a50')
...@@ -32,23 +32,23 @@ class Command(BaseCommand): ...@@ -32,23 +32,23 @@ class Command(BaseCommand):
default = True, default = True,
private_key_file = '/rosetta/.ssh/id_rsa', private_key_file = '/rosetta/.ssh/id_rsa',
public_key_file = '/rosetta/.ssh/id_rsa.pub') public_key_file = '/rosetta/.ssh/id_rsa.pub')
#=====================
#=====================
# Platform keys # Platform keys
#===================== #=====================
# TODO: create a different pair # TODO: create a different pair
try: try:
KeyPair.objects.get(user=None, default=True) KeyPair.objects.get(user=None, default=True)
print('Not creating default platform keys as they already exist') print('Not creating default platform keys as they already exist')
except KeyPair.DoesNotExist: except KeyPair.DoesNotExist:
print('Creating platform default keys') print('Creating platform default keys')
KeyPair.objects.create(user = None, KeyPair.objects.create(user = None,
default = True, default = True,
private_key_file = '/rosetta/.ssh/id_rsa', private_key_file = '/rosetta/.ssh/id_rsa',
public_key_file = '/rosetta/.ssh/id_rsa.pub') public_key_file = '/rosetta/.ssh/id_rsa.pub')
#===================== #=====================
# Default home page # Default home page
...@@ -60,7 +60,7 @@ class Command(BaseCommand): ...@@ -60,7 +60,7 @@ class Command(BaseCommand):
<h1>&nbsp;&nbsp;Rosetta <img src="/static/img/emoji_u1f6f0.png" style="height:84px; width:64px; padding-bottom:20px"></h1> <h1>&nbsp;&nbsp;Rosetta <img src="/static/img/emoji_u1f6f0.png" style="height:84px; width:64px; padding-bottom:20px"></h1>
<h2 style="margin-top:10px; margin-left:25px; margin-right:25px; font-weight:100; line-height: 30px;"><i>A container-centric Science Platform<br></i></h2> <h2 style="margin-top:10px; margin-left:25px; margin-right:25px; font-weight:100; line-height: 30px;"><i>A container-centric Science Platform<br></i></h2>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
<div class="dashboard"> <div class="dashboard">
<div class="span8 offset2" style="margin: 30px auto; max-width:800px"> <div class="span8 offset2" style="margin: 30px auto; max-width:800px">
...@@ -69,7 +69,7 @@ class Command(BaseCommand): ...@@ -69,7 +69,7 @@ class Command(BaseCommand):
This is the default main page content loaded after populating the platform with the default/demo data. This is the default main page content loaded after populating the platform with the default/demo data.
To change it, head to the <a href="/admin">admin</a> section and edit the <code>Page</code> model with id "main". To change it, head to the <a href="/admin">admin</a> section and edit the <code>Page</code> model with id "main".
<br/><br/> <br/><br/>
A test user with admin rights registered with email <code>testuser@rosetta.platform</code> and password A test user with admin rights registered with email <code>testuser@rosetta.platform</code> and password
<code>testpass</code> has been created as well, which you can use to login on the menu on the right and give Rosetta <code>testpass</code> has been created as well, which you can use to login on the menu on the right and give Rosetta
immediately a try. If you are using the default docker-compose file (i.e. you just ran <code>rosetta/setup</code>), immediately a try. If you are using the default docker-compose file (i.e. you just ran <code>rosetta/setup</code>),
then you will also have a few demo computing and storage resources (beside the internal one) already available then you will also have a few demo computing and storage resources (beside the internal one) already available
...@@ -79,10 +79,10 @@ class Command(BaseCommand): ...@@ -79,10 +79,10 @@ class Command(BaseCommand):
<br /> <br />
You can also create custom pages and access them under <code>/pages/page_id</code> should you need to provide You can also create custom pages and access them under <code>/pages/page_id</code> should you need to provide
your users informations about the platform and its storage and computing resources. For example, see this your users informations about the platform and its storage and computing resources. For example, see this
demo extra <a href="/pages/help">help page</a>. demo extra <a href="/pages/help">help page</a>.
</div> </div>
</div> </div>
</div> </div>
</header> </header>
''' '''
home_page = Page.objects.filter(id='main') home_page = Page.objects.filter(id='main')
...@@ -96,15 +96,15 @@ class Command(BaseCommand): ...@@ -96,15 +96,15 @@ class Command(BaseCommand):
<h1>Help!</h1> <h1>Help!</h1>
<hr> <hr>
<p> <p>
This is a demo extra page (a help page, in this case). Here you could for example provide the instructions on how to set up SSH-based This is a demo extra page (a help page, in this case). Here you could for example provide the instructions on how to set up SSH-based
computing resources using user keys, or who to contact to join a specific group to access its software and computing resources. computing resources using user keys, or who to contact to join a specific group to access its software and computing resources.
</p> </p>
<p> <p>
In general, the part of the URL following the <code>/pages/</code> path is parsed as the page id, In general, the part of the URL following the <code>/pages/</code> path is parsed as the page id,
so that if a page with that id exists in the database, its content will show up here. so that if a page with that id exists in the database, its content will show up here.
You can use this system for creating a mini-website inside the platform You can use this system for creating a mini-website inside the platform
to provide help, news and informations on your deployment. Or you can just ignore the whole thing and leave a plain logo in the main page. to provide help, news and informations on your deployment. Or you can just ignore the whole thing and leave a plain logo in the main page.
</p> </p>
''' '''
...@@ -117,16 +117,16 @@ to provide help, news and informations on your deployment. Or you can just ignor ...@@ -117,16 +117,16 @@ to provide help, news and informations on your deployment. Or you can just ignor
#===================== #=====================
# Platform containers # Platform containers
#===================== #=====================
platform_containers = Container.objects.filter(user=None) platform_containers = Container.objects.filter(user=None)
if platform_containers: if platform_containers:
print('Not creating public containers as they already exist') print('Not creating public containers as they already exist')
else: else:
print('Creating platform containers...') print('Creating platform containers...')
# Minimal Desktop # Minimal Desktop
Container.objects.create(user = None, Container.objects.create(user = None,
name = 'Minimal Desktop', name = 'Minimal Desktop',
...@@ -157,8 +157,8 @@ to provide help, news and informations on your deployment. Or you can just ignor ...@@ -157,8 +157,8 @@ to provide help, news and informations on your deployment. Or you can just ignor
supports_custom_interface_port = True, supports_custom_interface_port = True,
supports_interface_auth = True, supports_interface_auth = True,
interface_auth_user = None) interface_auth_user = None)
# Jupyter Notebook # Jupyter Notebook
Container.objects.create(user = None, Container.objects.create(user = None,
name = 'Jupyter Notebook', name = 'Jupyter Notebook',
...@@ -178,7 +178,7 @@ to provide help, news and informations on your deployment. Or you can just ignor ...@@ -178,7 +178,7 @@ to provide help, news and informations on your deployment. Or you can just ignor
# Official Jupyter containers # Official Jupyter containers
for tag in ['lab-3.2.2', 'lab-3.1.17']: for tag in ['lab-3.2.2', 'lab-3.1.17']:
Container.objects.create(user = None, Container.objects.create(user = None,
name = 'Jupyter Data Science Lab', name = 'Jupyter Data Science Lab',
description = 'The official Jupyter Lab. The Data Science variant, which includes libraries for data analysis from the Julia, Python, and R communities.', description = 'The official Jupyter Lab. The Data Science variant, which includes libraries for data analysis from the Julia, Python, and R communities.',
...@@ -192,7 +192,7 @@ to provide help, news and informations on your deployment. Or you can just ignor ...@@ -192,7 +192,7 @@ to provide help, news and informations on your deployment. Or you can just ignor
interface_transport = 'tcp/ip', interface_transport = 'tcp/ip',
supports_custom_interface_port = True, supports_custom_interface_port = True,
supports_interface_auth = True) supports_interface_auth = True)
for arch in ['amd64', 'arm64']: for arch in ['amd64', 'arm64']:
Container.objects.create(user = None, Container.objects.create(user = None,
name = 'Jupyter Lab', name = 'Jupyter Lab',
...@@ -225,9 +225,9 @@ to provide help, news and informations on your deployment. Or you can just ignor ...@@ -225,9 +225,9 @@ to provide help, news and informations on your deployment. Or you can just ignor
supports_interface_auth = True, supports_interface_auth = True,
interface_auth_user = 'metauser') interface_auth_user = 'metauser')
#===================== #=====================
# Testuser containers # Testuser containers
#===================== #=====================
#testuser_containers = Container.objects.filter(user=testuser) #testuser_containers = Container.objects.filter(user=testuser)
#if testuser_containers: #if testuser_containers:
# print('Not creating testuser private containers as they already exist') # print('Not creating testuser private containers as they already exist')
...@@ -250,9 +250,9 @@ to provide help, news and informations on your deployment. Or you can just ignor ...@@ -250,9 +250,9 @@ to provide help, news and informations on your deployment. Or you can just ignor
# supports_interface_auth = False) # supports_interface_auth = False)
#===================== #=====================
# Computing resources # Computing resources
#===================== #=====================
computing_resources = Computing.objects.all() computing_resources = Computing.objects.all()
if computing_resources: if computing_resources:
print('Not creating demo computing resources as they already exist') print('Not creating demo computing resources as they already exist')
...@@ -270,7 +270,7 @@ to provide help, news and informations on your deployment. Or you can just ignor ...@@ -270,7 +270,7 @@ to provide help, news and informations on your deployment. Or you can just ignor
wms = None, wms = None,
container_engines = ['docker']) container_engines = ['docker'])
# Demo standalone computing plus conf # Demo standalone computing plus conf
demo_standalone_computing = Computing.objects.create(name = 'Demo Standalone', demo_standalone_computing = Computing.objects.create(name = 'Demo Standalone',
description = 'A demo standalone computing resource.', description = 'A demo standalone computing resource.',
...@@ -311,22 +311,22 @@ to provide help, news and informations on your deployment. Or you can just ignor ...@@ -311,22 +311,22 @@ to provide help, news and informations on your deployment. Or you can just ignor
wms = 'slurm', wms = 'slurm',
conf = {'host': 'slurmclustermaster', 'default_partition': 'partition1'}, conf = {'host': 'slurmclustermaster', 'default_partition': 'partition1'},
container_engines = ['singularity']) container_engines = ['singularity'])
# Add testuser extra conf for this computing resource # Add testuser extra conf for this computing resource
testuser.profile.add_extra_conf(conf_type = 'computing_user', object=demo_slurm_computing, value= 'testuser') testuser.profile.add_extra_conf(conf_type = 'computing_user', object=demo_slurm_computing, value= 'testuser')
#===================== #=====================
# Storages # Storages
#===================== #=====================
storages = Storage.objects.all() storages = Storage.objects.all()
if storages: if storages:
print('Not creating demo storage resources as they already exist') print('Not creating demo storage resources as they already exist')
else: else:
print('Creating demo storage resources...') print('Creating demo storage resources...')
# Get demo computing resources # Get demo computing resources
demo_computing_resources = [] demo_computing_resources = []
try: try:
demo_slurm_computing = Computing.objects.get(name='Demo Cluster') demo_slurm_computing = Computing.objects.get(name='Demo Cluster')
demo_computing_resources.append(demo_slurm_computing) demo_computing_resources.append(demo_slurm_computing)
except: except:
...@@ -336,8 +336,8 @@ to provide help, news and informations on your deployment. Or you can just ignor ...@@ -336,8 +336,8 @@ to provide help, news and informations on your deployment. Or you can just ignor
demo_computing_resources.append(demo_standalone_computing) demo_computing_resources.append(demo_standalone_computing)
except: except:
pass pass
for computing in demo_computing_resources: for computing in demo_computing_resources:
# Demo shared storage # Demo shared storage
Storage.objects.create(computing = computing, Storage.objects.create(computing = computing,
...@@ -348,7 +348,7 @@ to provide help, news and informations on your deployment. Or you can just ignor ...@@ -348,7 +348,7 @@ to provide help, news and informations on your deployment. Or you can just ignor
auth_mode = 'user_keys', auth_mode = 'user_keys',
base_path = '/shared/data/shared', base_path = '/shared/data/shared',
bind_path = '/storages/shared') bind_path = '/storages/shared')
# Demo personal storage # Demo personal storage
Storage.objects.create(computing = computing, Storage.objects.create(computing = computing,
access_through_computing = True, access_through_computing = True,
...@@ -359,3 +359,4 @@ to provide help, news and informations on your deployment. Or you can just ignor ...@@ -359,3 +359,4 @@ to provide help, news and informations on your deployment. Or you can just ignor
base_path = '/shared/data/users/$SSH_USER', base_path = '/shared/data/users/$SSH_USER',
bind_path = '/storages/personal') bind_path = '/storages/personal')
...@@ -37,7 +37,7 @@ class TaskStatuses(object): ...@@ -37,7 +37,7 @@ class TaskStatuses(object):
# using an UUID pointing to some other model instead of the value in future, should this be necessary. # using an UUID pointing to some other model instead of the value in future, should this be necessary.
#========================= #=========================
# Profile # Profile
#========================= #=========================
class Profile(models.Model): class Profile(models.Model):
...@@ -68,25 +68,25 @@ class Profile(models.Model): ...@@ -68,25 +68,25 @@ class Profile(models.Model):
self.extra_confs = {} self.extra_confs = {}
self.extra_confs[str(uuid.uuid4())] = {'type': conf_type, 'object_uuid': str(object.uuid), 'value': value} self.extra_confs[str(uuid.uuid4())] = {'type': conf_type, 'object_uuid': str(object.uuid), 'value': value}
self.save() self.save()
def get_extra_conf(self, conf_type, object=None): def get_extra_conf(self, conf_type, object=None):
if self.extra_confs: if self.extra_confs:
for extra_conf in self.extra_confs: for extra_conf in self.extra_confs:
if conf_type == self.extra_confs[extra_conf]['type']: if conf_type == self.extra_confs[extra_conf]['type']:
if object: if object:
#logger.debug("{} vs {}".format(self.extra_confs[extra_conf]['object_uuid'], str(object.uuid))) #logger.debug("{} vs {}".format(self.extra_confs[extra_conf]['object_uuid'], str(object.uuid)))
if self.extra_confs[extra_conf]['object_uuid'] == str(object.uuid): if self.extra_confs[extra_conf]['object_uuid'] == str(object.uuid):
return self.extra_confs[extra_conf]['value'] return self.extra_confs[extra_conf]['value']
else: else:
return self.extra_confs[extra_conf]['value'] return self.extra_confs[extra_conf]['value']
return None return None
#========================= #=========================
# Login Token # Login Token
#========================= #=========================
class LoginToken(models.Model): class LoginToken(models.Model):
...@@ -106,7 +106,7 @@ class LoginToken(models.Model): ...@@ -106,7 +106,7 @@ class LoginToken(models.Model):
class Container(models.Model): class Container(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, related_name='containers', on_delete=models.CASCADE, blank=True, null=True) user = models.ForeignKey(User, related_name='containers', on_delete=models.CASCADE, blank=True, null=True)
# If a container has no user, it will be available to anyone. Can be created, edited and deleted only by admins. # If a container has no user, it will be available to anyone. Can be created, edited and deleted only by admins.
group = models.ForeignKey(Group, related_name='containers', on_delete=models.CASCADE, blank=True, null=True) group = models.ForeignKey(Group, related_name='containers', on_delete=models.CASCADE, blank=True, null=True)
# If a container has no group, it will be available to anyone. Can be created, edited and deleted only by admins. # If a container has no group, it will be available to anyone. Can be created, edited and deleted only by admins.
...@@ -114,25 +114,25 @@ class Container(models.Model): ...@@ -114,25 +114,25 @@ class Container(models.Model):
# Generic attributes # Generic attributes
name = models.CharField('Name', max_length=255, blank=False, null=False) name = models.CharField('Name', max_length=255, blank=False, null=False)
description = models.TextField('Description', blank=True, null=True) description = models.TextField('Description', blank=True, null=True)
# Registry # Registry
registry = models.CharField('Registry', max_length=255, blank=False, null=False) registry = models.CharField('Registry', max_length=255, blank=False, null=False)
# Image name # Image name
image_name = models.CharField('Image', max_length=255, blank=False, null=False) image_name = models.CharField('Image', max_length=255, blank=False, null=False)
# Image identifiers # Image identifiers
image_tag = models.CharField('Tag', max_length=255, blank=False, null=False, default='latest') image_tag = models.CharField('Tag', max_length=255, blank=False, null=False, default='latest')
image_arch = models.CharField('Architecture', max_length=36, blank=True, null=True) image_arch = models.CharField('Architecture', max_length=36, blank=True, null=True)
image_os = models.CharField('Operating system', max_length=36, blank=True, null=True) image_os = models.CharField('Operating system', max_length=36, blank=True, null=True)
# -- OR -- # -- OR --
image_digest = models.CharField('SHA 256 digest', max_length=96, blank=True, null=True) image_digest = models.CharField('SHA 256 digest', max_length=96, blank=True, null=True)
# TODO: do we want more control with respect to kernel, CPUs, instruction sets? # TODO: do we want more control with respect to kernel, CPUs, instruction sets?
# requires = i.e. kernel > 3, intel, AVX2 # requires = i.e. kernel > 3, intel, AVX2
# Port, protocol and transport for the container interface # Port, protocol and transport for the container interface
interface_port = models.IntegerField('Interface port', blank=True, null=True) interface_port = models.IntegerField('Interface port', blank=True, null=True)
interface_protocol = models.CharField('Interface protocol', max_length=36, blank=True, null=True) interface_protocol = models.CharField('Interface protocol', max_length=36, blank=True, null=True)
interface_transport = models.CharField('Interface transport', max_length=36, blank=True, null=True) interface_transport = models.CharField('Interface transport', max_length=36, blank=True, null=True)
...@@ -156,7 +156,7 @@ class Container(models.Model): ...@@ -156,7 +156,7 @@ class Container(models.Model):
# Check that digest starts with sha256: # Check that digest starts with sha256:
if self.image_digest and not self.image_digest.startswith('sha256:'): if self.image_digest and not self.image_digest.startswith('sha256:'):
raise ValueError('The digest field must start with "sha256:"') raise ValueError('The digest field must start with "sha256:"')
super(Container, self).save(*args, **kwargs) super(Container, self).save(*args, **kwargs)
@property @property
...@@ -183,7 +183,7 @@ class Computing(models.Model): ...@@ -183,7 +183,7 @@ class Computing(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
group = models.ForeignKey(Group, related_name='computings', on_delete=models.CASCADE, blank=True, null=True) group = models.ForeignKey(Group, related_name='computings', on_delete=models.CASCADE, blank=True, null=True)
# If a compute resource has no group, it will be available to anyone. Can be created, edited and deleted only by admins. # If a compute resource has no group, it will be available to anyone. Can be created, edited and deleted only by admins.
name = models.CharField('Name', max_length=255, blank=False, null=False) name = models.CharField('Name', max_length=255, blank=False, null=False)
description = models.TextField('Description', blank=True, null=True) description = models.TextField('Description', blank=True, null=True)
...@@ -195,16 +195,16 @@ class Computing(models.Model): ...@@ -195,16 +195,16 @@ class Computing(models.Model):
access_mode = models.CharField('Access (control) mode', max_length=36, blank=False, null=False) access_mode = models.CharField('Access (control) mode', max_length=36, blank=False, null=False)
auth_mode = models.CharField('Auth mode', max_length=36, blank=False, null=False) auth_mode = models.CharField('Auth mode', max_length=36, blank=False, null=False)
wms = models.CharField('Workload management system', max_length=36, blank=True, null=True) wms = models.CharField('Workload management system', max_length=36, blank=True, null=True)
# Supported container engines (e.g. ['docker', 'singularity', 'podman']) # Supported container engines (e.g. ['docker', 'singularity', 'podman'])
container_engines = JSONField('Container engines/runtimes', blank=False, null=False) container_engines = JSONField('Container engines/runtimes', blank=False, null=False)
#container_runtime = models.CharField('Container runtimes', max_length=256, blank=False, null=False) #container_runtime = models.CharField('Container runtimes', max_length=256, blank=False, null=False)
# Supported architectures (i.e. 386 for amd64), as list: ['386'] # Supported architectures (i.e. 386 for amd64), as list: ['386']
supported_archs = JSONField('Supported architectures', blank=True, null=True) supported_archs = JSONField('Supported architectures', blank=True, null=True)
# Emulated architectures, by container runtime: {'docker': ['arm64/v7', 'arm64/v8'] # Emulated architectures, by container runtime: {'docker': ['arm64/v7', 'arm64/v8']
emulated_archs = JSONField('Emulated architectures', blank=True, null=True) emulated_archs = JSONField('Emulated architectures', blank=True, null=True)
# Conf # Conf
conf = JSONField(blank=True, null=True) conf = JSONField(blank=True, null=True)
...@@ -233,23 +233,23 @@ class Computing(models.Model): ...@@ -233,23 +233,23 @@ class Computing(models.Model):
@property @property
def default_container_engine(self): def default_container_engine(self):
return self.container_engines[0] return self.container_engines[0]
#======================= #=======================
# Computing manager # Computing manager
#======================= #=======================
@property @property
def manager(self): def manager(self):
from . import computing_managers from . import computing_managers
# Hash table mapping # Hash table mapping
managers_mapping = {} managers_mapping = {}
managers_mapping['cluster'+'ssh+cli'+'user_keys'+'slurm'] = computing_managers.SlurmSSHClusterComputingManager managers_mapping['cluster'+'ssh+cli'+'user_keys'+'slurm'] = computing_managers.SlurmSSHClusterComputingManager
managers_mapping['standalone'+'ssh+cli'+'user_keys'+'None'] = computing_managers.SSHStandaloneComputingManager managers_mapping['standalone'+'ssh+cli'+'user_keys'+'None'] = computing_managers.SSHStandaloneComputingManager
managers_mapping['standalone'+'ssh+cli'+'platform_keys'+'None'] = computing_managers.SSHStandaloneComputingManager managers_mapping['standalone'+'ssh+cli'+'platform_keys'+'None'] = computing_managers.SSHStandaloneComputingManager
managers_mapping['standalone'+'internal'+'internal'+'None'] = computing_managers.InternalStandaloneComputingManager managers_mapping['standalone'+'internal'+'internal'+'None'] = computing_managers.InternalStandaloneComputingManager
# Instantiate the computing manager and return (if not already done) # Instantiate the computing manager and return (if not already done)
try: try:
return self._manager return self._manager
...@@ -262,10 +262,10 @@ class Computing(models.Model): ...@@ -262,10 +262,10 @@ class Computing(models.Model):
else: else:
return self._manager return self._manager
#========================= #=========================
# Tasks # Tasks
#========================= #=========================
class Task(models.Model): class Task(models.Model):
...@@ -277,16 +277,16 @@ class Task(models.Model): ...@@ -277,16 +277,16 @@ class Task(models.Model):
# Task management # Task management
status = models.CharField('Status', max_length=36, blank=True, null=True) status = models.CharField('Status', max_length=36, blank=True, null=True)
created = models.DateTimeField('Created on', default=timezone.now) created = models.DateTimeField('Created on', default=timezone.now)
process_id = models.CharField('Process ID', max_length=64, blank=True, null=True) # i.e. Singularity PID process_id = models.CharField('Process ID', max_length=64, blank=True, null=True) # i.e. Singularity PID
job_id = models.CharField('Job ID', max_length=64, blank=True, null=True) # i.e. Slurm job id job_id = models.CharField('Job ID', max_length=64, blank=True, null=True) # i.e. Slurm job id
# How to reach the task interface. The IP has to be intended either as the container IP if this is directly # How to reach the task interface. The IP has to be intended either as the container IP if this is directly
# reachable (i.e. using a Docker or Kubernetes network) or as the host IP address, depending on the # reachable (i.e. using a Docker or Kubernetes network) or as the host IP address, depending on the
# computing resource and its computing manager/WMS/container runtime. The port is to be intended # computing resource and its computing manager/WMS/container runtime. The port is to be intended
# as the port where the task interface is exposed on its IP address. # as the port where the task interface is exposed on its IP address.
interface_ip = models.CharField('Interface IP address', max_length=36, blank=True, null=True) interface_ip = models.CharField('Interface IP address', max_length=36, blank=True, null=True)
interface_port = models.IntegerField('Interface port', blank=True, null=True) interface_port = models.IntegerField('Interface port', blank=True, null=True)
# Task access # Task access
requires_tcp_tunnel = models.BooleanField('Requires TCP tunnel') requires_tcp_tunnel = models.BooleanField('Requires TCP tunnel')
tcp_tunnel_port = models.IntegerField('TCP tunnel port', blank=True, null=True) tcp_tunnel_port = models.IntegerField('TCP tunnel port', blank=True, null=True)
...@@ -301,13 +301,13 @@ class Task(models.Model): ...@@ -301,13 +301,13 @@ class Task(models.Model):
# Computing options # Computing options
# TODO: add the option for selecting the runtime as advanced option when creating the task? # TODO: add the option for selecting the runtime as advanced option when creating the task?
computing_options = JSONField('Computing options', blank=True, null=True) # i.e. CPUs, RAM, cluster partition etc. TODO: why here? computing_options = JSONField('Computing options', blank=True, null=True) # i.e. CPUs, RAM, cluster partition etc. TODO: why here?
class Meta: class Meta:
ordering = ['-created'] ordering = ['-created']
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
try: try:
getattr(TaskStatuses, str(self.status)) getattr(TaskStatuses, str(self.status))
except AttributeError: except AttributeError:
...@@ -318,25 +318,25 @@ class Task(models.Model): ...@@ -318,25 +318,25 @@ class Task(models.Model):
def update_status(self): def update_status(self):
if self.computing == 'local': if self.computing == 'local':
check_command = 'sudo docker inspect --format \'{{.State.Status}}\' ' + self.tid # or, .State.Running check_command = 'sudo docker inspect --format \'{{.State.Status}}\' ' + self.tid # or, .State.Running
out = os_shell(check_command, capture=True) out = os_shell(check_command, capture=True)
logger.debug('Status: "{}"'.format(out.stdout)) logger.debug('Status: "{}"'.format(out.stdout))
if out.exit_code != 0: if out.exit_code != 0:
if (('No such' in out.stderr) and (self.tid in out.stderr)): if (('No such' in out.stderr) and (self.tid in out.stderr)):
logger.debug('Task "{}" is not running in reality'.format(self.tid)) logger.debug('Task "{}" is not running in reality'.format(self.tid))
self.status = TaskStatuses.exited self.status = TaskStatuses.exited
else: else:
if out.stdout == 'running': if out.stdout == 'running':
self.status = TaskStatuses.running self.status = TaskStatuses.running
elif out.stdout == 'exited': elif out.stdout == 'exited':
self.status = TaskStatuses.exited self.status = TaskStatuses.exited
else: else:
raise Exception('Unknown task status: "{}"'.format(out.stdout)) raise Exception('Unknown task status: "{}"'.format(out.stdout))
self.save() self.save()
def __str__(self): def __str__(self):
...@@ -351,11 +351,11 @@ class Task(models.Model): ...@@ -351,11 +351,11 @@ class Task(models.Model):
string_int_hash = hash_string_to_int(self.name) string_int_hash = hash_string_to_int(self.name)
color_map_index = string_int_hash % len(color_map) color_map_index = string_int_hash % len(color_map)
return color_map[color_map_index] return color_map[color_map_index]
@property @property
def sharable_link(self): def sharable_link(self):
return 'https://{}/t/{}'.format(settings.ROSETTA_HOST, self.short_uuid) return 'https://{}/t/{}'.format(settings.ROSETTA_HOST, self.short_uuid)
@property @property
def tcp_tunnel_host(self): def tcp_tunnel_host(self):
return get_rosetta_tasks_tunnel_host() return get_rosetta_tasks_tunnel_host()
...@@ -369,61 +369,65 @@ class Task(models.Model): ...@@ -369,61 +369,65 @@ class Task(models.Model):
#========================= #=========================
class Storage(models.Model): class Storage(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
group = models.ForeignKey(Group, related_name='storages', on_delete=models.CASCADE, blank=True, null=True) group = models.ForeignKey(Group, related_name='storages', on_delete=models.CASCADE, blank=True, null=True)
name = models.CharField('Name', max_length=255, blank=False, null=False) name = models.CharField('Name', max_length=255, blank=False, null=False)
#description = models.TextField('Description', blank=True, null=True) #description = models.TextField('Description', blank=True, null=True)
# Storage type # Storage type
type = models.CharField('Type', max_length=255, blank=False, null=False) type = models.CharField('Type', max_length=255, blank=False, null=False)
# Access and auth mode # Access and auth mode
access_mode = models.CharField('Access (control) mode', max_length=36, blank=False, null=False) access_mode = models.CharField('Access (control) mode', max_length=36, blank=False, null=False)
auth_mode = models.CharField('Auth mode', max_length=36, blank=False, null=False) auth_mode = models.CharField('Auth mode', max_length=36, blank=False, null=False)
# Paths # Paths
base_path = models.CharField('Base path', max_length=4096, blank=False, null=False) base_path = models.CharField('Base path', max_length=4096, blank=False, null=False)
bind_path = models.CharField('Bind path', max_length=4096, blank=True, null=True) bind_path = models.CharField('Bind path', max_length=4096, blank=True, null=True)
# Link with a computing resource # Link with a computing resource
computing = models.ForeignKey(Computing, related_name='storages', on_delete=models.CASCADE, blank=True, null=True) # Make optional? computing = models.ForeignKey(Computing, related_name='storages', on_delete=models.CASCADE, blank=True, null=True) # Make optional?
access_through_computing = models.BooleanField('Access through linked computing resource?', default=False) access_through_computing = models.BooleanField('Access through linked computing resource?', default=False)
# If the above is linked, some configuration can be taken from the linked computing resource (i.e. the hostname) # If the above is linked, some configuration can be taken from the linked computing resource (i.e. the hostname)
# Configuration # Configuration
conf = JSONField(blank=True, null=True) conf = JSONField(blank=True, null=True)
# Include as browsable in the file manager? # Include as browsable in the file manager?
browsable = models.BooleanField('Browsable in the file manager?', default=True) browsable = models.BooleanField('Browsable in the file manager?', default=True)
def save(self, *args, **kwargs):
if self.access_mode == 'internal' and self.browsable:
raise ValueError('A storage with "internal" access mode cannot be marked as browsable since it is not yet supported by the file manager')
super(Storage, self).save(*args, **kwargs)
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
def __str__(self): def __str__(self):
if self.group: if self.group:
return str('Storage "{}" of group "{}"'.format(self.id, self.group)) return str('Storage "{}" of group "{}"'.format(self.id, self.group))
else: else:
return str('Storage "{}"'.format(self.id)) return str('Storage "{}"'.format(self.id))
@property @property
def id(self): def id(self):
return (self.name if not self.computing else '{}:{}'.format(self.computing.name,self.name)) return (self.name if not self.computing else '{}:{}'.format(self.computing.name,self.name))
#========================= #=========================
# KeyPair # KeyPair
#========================= #=========================
class KeyPair(models.Model): class KeyPair(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, related_name='key_pairs', on_delete=models.CASCADE, blank=True, null=True) user = models.ForeignKey(User, related_name='key_pairs', on_delete=models.CASCADE, blank=True, null=True)
private_key_file = models.CharField('Private key file', max_length=4096, blank=False, null=False) private_key_file = models.CharField('Private key file', max_length=4096, blank=False, null=False)
public_key_file = models.CharField('Public key file', max_length=4096, blank=False, null=False) public_key_file = models.CharField('Public key file', max_length=4096, blank=False, null=False)
...@@ -436,12 +440,12 @@ class KeyPair(models.Model): ...@@ -436,12 +440,12 @@ class KeyPair(models.Model):
return str('KeyPair of user "{}" (default={})'.format( self.user.email, self.default)) return str('KeyPair of user "{}" (default={})'.format( self.user.email, self.default))
else: else:
return str('KeyPair of user Platform (default={})'.format(self.default)) return str('KeyPair of user Platform (default={})'.format(self.default))
#========================= #=========================
# Page # Page
#========================= #=========================
class Page(models.Model): class Page(models.Model):
...@@ -457,3 +461,4 @@ class Page(models.Model): ...@@ -457,3 +461,4 @@ class Page(models.Model):
@keyframes spin {
to {
transform: rotate(360deg);
}
}
#navigate-away-loader {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 2000;
align-items: center;
justify-content: center;
color: white;
transition: opacity 0.4s ease-in-out;
visibility: hidden;
pointer-events: none;
display: flex;
& > svg {
animation: spin 1.5s infinite linear;
opacity: 0.5;
}
}
function showNavigationLoader() {
var navigationLoader = document.querySelector("#navigate-away-loader")
navigationLoader.style.visibility = "visible"
navigationLoader.style.opacity = 1
navigationLoader.style.pointerEvents = "all"
document.querySelector("#navigate-away-loader > svg").style.animation =
"spin 1.5s infinite linear"
}
function hideNavigationLoader() {
var navigationLoader = document.querySelector("#navigate-away-loader")
navigationLoader.style.visibility = "hidden"
navigationLoader.style.opacity = 0
navigationLoader.style.pointerEvents = "none"
}
window.addEventListener("beforeunload", function (e) {
showNavigationLoader()
return true
})
window.addEventListener("pageshow", function (e) {
hideNavigationLoader()
})
{% load static %} {% load static %}
{% include "header.html" %} {% include "header.html" %}
{% include "navigation.html" %} {% include "navigation.html" %}
{% include "logo.html" %} {% include "logo.html" %}
...@@ -23,24 +23,24 @@ ...@@ -23,24 +23,24 @@
{{data.user.username}} {{data.user.username}}
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<b>Email</b> <b>Email</b>
</td> </td>
<td> <td>
{% if data.edit == 'email' %} {% if data.edit == 'email' %}
<table class="frametab"><tr><td> <table class="frametab"><tr><td>
<input type='hidden' name='edit' value='email' /> <input type='hidden' name='edit' value='email' />
<input type="email" value="{{data.user.email}}" name='value' style='width:95%' required autofocus /> <input type="email" value="{{data.user.email}}" name='value' style='width:95%' required autofocus />
</td><td> </td><td>
<input type="submit" value="Go"> <input type="submit" value="Go">
</td></tr></table> </td></tr></table>
{% else %} {% else %}
{{data.user.email}}{% if data.user.profile.auth == 'local' %} | <a href="/account/?edit=email">Change</a>{% endif %} {{data.user.email}}{% if data.user.profile.auth == 'local' %} | <a href="/account/?edit=email">Change</a>{% endif %}
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
{% if data.user.profile.auth == 'local' %} {% if data.user.profile.auth == 'local' %}
<tr> <tr>
...@@ -48,19 +48,19 @@ ...@@ -48,19 +48,19 @@
<b>Password</b> <b>Password</b>
</td> </td>
<td> <td>
{% if data.edit == 'password' %} {% if data.edit == 'password' %}
<table class="frametab"><tr><td> <table class="frametab"><tr><td>
<input type='hidden' name='edit' value='password' /> <input type='hidden' name='edit' value='password' />
<input type="password" name='value' style='width:95%' required autofocus /> <input type="password" name='value' style='width:95%' required autofocus />
</td><td> </td><td>
<input type="submit" value="Go"> <input type="submit" value="Go">
</td></tr></table> </td></tr></table>
{% else %} {% else %}
******* | <a href="/account/?edit=password">Change</a> ******* | <a href="/account/?edit=password">Change</a>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
<tr> <tr>
...@@ -75,24 +75,24 @@ ...@@ -75,24 +75,24 @@
{% else %} {% else %}
{{ data.user.profile.auth }} {{ data.user.profile.auth }}
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
</table> </table>
<br /> <br />
<h3>Profile</h3> <h3>Profile</h3>
<table class="dashboard"> <table class="dashboard">
<tr> <tr>
<td> <td>
<b>Time Zone</b> <b>Time Zone</b>
</td> </td>
<td> <td>
{% if data.edit == 'timezone' %} {% if data.edit == 'timezone' %}
{% include "components/tzselect.html" %} {% include "components/tzselect.html" %}
<input type='hidden' name='edit' value='timezone' /> <input type='hidden' name='edit' value='timezone' />
<input type="submit" value="Go"> <input type="submit" value="Go">
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
<tr> <tr>
<td valign=top> <td valign=top>
...@@ -117,23 +117,23 @@ ...@@ -117,23 +117,23 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
: <code>{{conf_data.value}}</code> | <a href='?delete_extra_conf_uuid={{conf_uuid}}'>delete</a> : <code>{{conf_data.value}}</code> | <a href='?delete_extra_conf_uuid={{conf_uuid}}'>delete</a>
<br/> <br/>
{% endfor %} {% endfor %}
<span style="margin:3px"><a href="/add_profile_conf" style="line-height:2em">Add new...</a></span> <span style="margin:3px"><a href="/add_profile_conf" style="line-height:2em">Add new...</a></span>
</td> </td>
</tr> </tr>
</table> </table>
<br /> <br />
<h3>KeyPair</h3> <h3>KeyPair</h3>
<table class="dashboard"> <table class="dashboard">
<tr> <tr>
<td valign="top"> <td valign="top">
<b>Default public key</b> <b>Default public key</b>
...@@ -145,7 +145,7 @@ ...@@ -145,7 +145,7 @@
</table> </table>
</form> </form>
<div style="margin-left:10px; margin-top:40px"> <div style="margin-left:10px; margin-top:40px">
{% if data.user.profile.auth == 'oidc' %} {% if data.user.profile.auth == 'oidc' %}
<form action="{% url 'oidc_logout' %}" method="post"> <form action="{% url 'oidc_logout' %}" method="post">
...@@ -155,18 +155,18 @@ ...@@ -155,18 +155,18 @@
{% else %} {% else %}
<form action="/logout/" method="get"> <form action="/logout/" method="get">
<input type="submit" value="logout"> <input type="submit" value="logout">
</form> </form>
{% endif %} {% endif %}
</div> </div>
<br/> <br/>
<br/> <br/>
<br/> <br/>
<br/> <br/>
<br/> <br/>
<br/> <br/>
</div> </div>
</div> </div>
</div> </div>
...@@ -178,3 +178,4 @@ ...@@ -178,3 +178,4 @@
{% load static %} {% load static %}
{% include "header.html" %} {% include "header.html" %}
{% include "navigation.html" %} {% include "navigation.html" %}
{% include "logo.html" %} {% include "logo.html" %}
...@@ -6,19 +6,19 @@ ...@@ -6,19 +6,19 @@
<div class="container"> <div class="container">
<div class="dashboard"> <div class="dashboard">
<div class="span8 offset2"> <div class="span8 offset2">
<h1>Add extra profile configuration</h1> <h1>Add extra profile configuration</h1>
<hr> <hr>
<h4>Chose the configuration type and add the values</h4> <h4>Chose the configuration type and add the values</h4>
<br/> <br/>
<form action="/add_profile_conf/" method="POST"> <form action="/add_profile_conf/" method="POST">
{% csrf_token %} {% csrf_token %}
<table class="dashboard" style="max-width:430px"> <table class="dashboard" style="max-width:430px">
<tr> <tr>
<td> <td>
{%if data.conf_type %} {%if data.conf_type %}
<select name="conf_type"> <select name="conf_type">
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
{% for conf_type in data.conf_types %} {% for conf_type in data.conf_types %}
<option value="{{conf_type}}">{{conf_type}}</option> <option value="{{conf_type}}">{{conf_type}}</option>
{% endfor %} {% endfor %}
</select> </select>
{% endif %} {% endif %}
</td> </td>
...@@ -44,11 +44,11 @@ ...@@ -44,11 +44,11 @@
{% for computing in data.computings %} {% for computing in data.computings %}
<option value="{{computing.uuid}}">{{computing.name}}</option> <option value="{{computing.uuid}}">{{computing.name}}</option>
{% endfor %} {% endfor %}
</select> </select>
{% endif %} {% endif %}
</td> </td>
{% endif %} {% endif %}
{% if data.last_step %} {% if data.last_step %}
<td colspan=2 align=center style="padding:20px"> <td colspan=2 align=center style="padding:20px">
...@@ -62,11 +62,10 @@ ...@@ -62,11 +62,10 @@
<input type="submit" value="Next"> <input type="submit" value="Next">
</td> </td>
{% endif %} {% endif %}
</table> </table>
</form> </form>
<br/>
<br/> <br/>
<br/> <br/>
<br/> <br/>
...@@ -75,7 +74,8 @@ ...@@ -75,7 +74,8 @@
<br/> <br/>
<br/> <br/>
<br/> <br/>
<br/>
</div> </div>
</div> </div>
</div> </div>
...@@ -87,3 +87,4 @@ ...@@ -87,3 +87,4 @@
{% load static %} {% load static %}
{% include "header.html" %} {% include "header.html" %}
{% include "navigation.html" %} {% include "navigation.html" %}
{% include "logo.html" %} {% include "logo.html" %}
...@@ -6,24 +6,24 @@ ...@@ -6,24 +6,24 @@
<div class="container"> <div class="container">
<div class="dashboard"> <div class="dashboard">
<div class="span8 offset2"> <div class="span8 offset2">
<h1>Add software container</h1> <h1>Add software container</h1>
<hr> <hr>
{% if not data.added %} {% if not data.added %}
Here you can add a new software container on the platform. You can add containers from image registries Here you can add a new software container on the platform. You can add containers from image registries
as <a href="https://hub.docker.com/">Docker Hub </a>or by importing Git repositories, provided that they as <a href="https://hub.docker.com/">Docker Hub </a>or by importing Git repositories, provided that they
are compatible with <a href="https://mybinder.readthedocs.io/en/latest/introduction.html">Binder</a> specifications. are compatible with <a href="https://mybinder.readthedocs.io/en/latest/introduction.html">Binder</a> specifications.
<br/> <br/>
<br/> <br/>
{% if data.new_container_from == 'registry' %} {% if data.new_container_from == 'registry' %}
<div style="font-size:1.1em; background:whitesmoke; display:inline-block; padding:2px 15px 2px 15px">New container from registry</div> <div style="font-size:1.1em; background:whitesmoke; display:inline-block; padding:2px 15px 2px 15px">New container from registry</div>
<div style="font-size:1.1em; background:white; display:inline-block; padding:2px 15px 2px 15px"><a href="?new_container_from=repository">New container from Git repository</a></div> <div style="font-size:1.1em; background:white; display:inline-block; padding:2px 15px 2px 15px"><a href="?new_container_from=repository">New container from Git repository</a></div>
<hr style="margin-top:0;"> <hr style="margin-top:0;">
<h4>Basics</h4> <h4>Basics</h4>
<form action="#" method="POST"> <form action="#" method="POST">
{% csrf_token %} {% csrf_token %}
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
<textarea name="container_description" rows="3" cols="22"></textarea> <textarea name="container_description" rows="3" cols="22"></textarea>
</td> </td>
</tr> </tr>
<tr> <tr>
<td><b>Registry</b></td><td> <td><b>Registry</b></td><td>
<input type="text" name="container_registry" value="docker.io" size="23" required /> <input type="text" name="container_registry" value="docker.io" size="23" required />
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
<h4>Interface </h4> <h4>Interface </h4>
<table class="dashboard" style="width:400px; margin-bottom:25px"> <table class="dashboard" style="width:400px; margin-bottom:25px">
<tr> <tr>
...@@ -80,7 +80,7 @@ ...@@ -80,7 +80,7 @@
<td><b>Interface protocol</b></td> <td><b>Interface protocol</b></td>
<td> <td>
{% if request.user.profile.is_power_user %} {% if request.user.profile.is_power_user %}
<input type="text" value="http" name="container_interface_protocol" size="5" /> <input type="text" value="http" name="container_interface_protocol" size="5" />
{% else %} {% else %}
<select name="container_interface_protocol" > <select name="container_interface_protocol" >
<option value="http" selected>http</option> <option value="http" selected>http</option>
...@@ -91,13 +91,13 @@ ...@@ -91,13 +91,13 @@
</tr> </tr>
</table> </table>
<a href="javascript:void(0);" id="show_button" onclick="toggle_visibility('advanced_div')">Advanced...</a> <a href="javascript:void(0);" id="show_button" onclick="toggle_visibility('advanced_div')">Advanced...</a>
<div id="advanced_div" style="display:none; width:400px;"> <div id="advanced_div" style="display:none; width:400px;">
<h4>Advanced <font size=-1>| <a href="javascript:void(0);" id="hide_button" onclick="toggle_visibility('advanced_div')" style="display:none">hide</a></font></h4> <h4>Advanced <font size=-1>| <a href="javascript:void(0);" id="hide_button" onclick="toggle_visibility('advanced_div')" style="display:none">hide</a></font></h4>
<table class="dashboard" style="width:400px; margin-bottom:25px"> <table class="dashboard" style="width:400px; margin-bottom:25px">
<tr> <tr>
...@@ -130,21 +130,21 @@ ...@@ -130,21 +130,21 @@
<tr> <tr>
<td colspan=2> <td colspan=2>
<b>Supports custom interface port</b> &nbsp; <b>Supports custom interface port</b> &nbsp;
<input type="checkbox" name="container_supports_custom_interface_port" value="True" /> <input type="checkbox" name="container_supports_custom_interface_port" value="True" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan=2> <td colspan=2>
<b>Supports interface auth</b> &nbsp; <b>Supports interface auth</b> &nbsp;
<input type="checkbox" name="container_supports_interface_auth" value="True" /> <input type="checkbox" name="container_supports_interface_auth" value="True" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan=2> <td colspan=2>
<b>Disable HTTP auth embedding</b> &nbsp; <b>Disable HTTP auth embedding</b> &nbsp;
<input type="checkbox" name="container_disable_http_basicauth_embedding" value="True" /> <input type="checkbox" name="container_disable_http_basicauth_embedding" value="True" />
</td> </td>
</tr> </tr>
...@@ -158,7 +158,7 @@ ...@@ -158,7 +158,7 @@
</table> </table>
</div> </div>
<table style="width:400px; border:0; background:#ffffff; margin-top:20px"> <table style="width:400px; border:0; background:#ffffff; margin-top:20px">
<tr><td align="center"> <tr><td align="center">
<input type="submit" value="Add"> <input type="submit" value="Add">
...@@ -166,17 +166,17 @@ ...@@ -166,17 +166,17 @@
</table> </table>
<input type="hidden" name="new_container_from" value="registry"> <input type="hidden" name="new_container_from" value="registry">
</form> </form>
{% else %} {% else %}
<div style="font-size:1.1em; background:white; display:inline-block; padding:2px 15px 2px 15px"><a href="?new_container_from=registry">New container from registry</a></div> <div style="font-size:1.1em; background:white; display:inline-block; padding:2px 15px 2px 15px"><a href="?new_container_from=registry">New container from registry</a></div>
<div style="font-size:1.1em; background:whitesmoke; display:inline-block; padding:2px 15px 2px 15px">New container from Git repository</div> <div style="font-size:1.1em; background:whitesmoke; display:inline-block; padding:2px 15px 2px 15px">New container from Git repository</div>
<hr style="margin-top:0;"> <hr style="margin-top:0;">
<h4>Basics</h4> <h4>Basics</h4>
<form action="#" method="POST"> <form action="#" method="POST">
{% csrf_token %} {% csrf_token %}
...@@ -196,7 +196,7 @@ ...@@ -196,7 +196,7 @@
<textarea name="container_description" rows="3" cols="22"></textarea> <textarea name="container_description" rows="3" cols="22"></textarea>
</td> </td>
</tr> </tr>
<tr> <tr>
<td><b>Repository URL</b></td><td> <td><b>Repository URL</b></td><td>
<input type="text" name="repository_url" size="23" required /> <input type="text" name="repository_url" size="23" required />
...@@ -214,24 +214,23 @@ ...@@ -214,24 +214,23 @@
</table> </table>
<table style="width:400px; border:0; background:#ffffff; margin-top:20px"> <table style="width:400px; border:0; background:#ffffff; margin-top:20px">
<tr><td align="center"> <tr><td align="center">
<input type="submit" value="Add"> <input type="submit" value="Add">
</td></tr> </td></tr>
</table> </table>
<input type="hidden" name="new_container_from" value="repository"> <input type="hidden" name="new_container_from" value="repository">
</form> </form>
{% endif %} {% endif %}
<br/>
<br/> <br/>
<br/> <br/>
<br/> <br/>
...@@ -240,21 +239,22 @@ ...@@ -240,21 +239,22 @@
<br/> <br/>
<br/> <br/>
<br/> <br/>
<br/>
{% else %} {% else %}
Ok, software container added. Go to <a href="/software">software</a>. Ok, software container added. Go to <a href="/software">software</a>.
{% endif %}
{% endif %}
<br/> <br/>
<br/> <br/>
<br/> <br/>
<br/> <br/>
<br/> <br/>
<br/> <br/>
</div> </div>
</div> </div>
</div> </div>
...@@ -270,7 +270,7 @@ function toggle_visibility(id) { ...@@ -270,7 +270,7 @@ function toggle_visibility(id) {
x.style.display = "block"; x.style.display = "block";
y.style.display = "none" y.style.display = "none"
z.style.display= "inline" z.style.display= "inline"
} else { } else {
x.style.display = "none"; x.style.display = "none";
y.style.display= "inline" y.style.display= "inline"
...@@ -283,3 +283,4 @@ function toggle_visibility(id) { ...@@ -283,3 +283,4 @@ function toggle_visibility(id) {
...@@ -13,10 +13,10 @@ ...@@ -13,10 +13,10 @@
<div style="color:green"> <div style="color:green">
{{data.success}} {{data.success}}
</div> </div>
{% else %} {% else %}
<br/> <br/>
{% endif %} {% endif %}
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
Logged in as <b>{{ request.user.username }}</b> | <a href="/logout">Logout</a> Logged in as <b>{{ request.user.username }}</b> | <a href="/logout">Logout</a>
{% else %} {% else %}
...@@ -41,11 +41,12 @@ ...@@ -41,11 +41,12 @@
<li><a href="/api/v1/base/login/">/api/v1/base/login/</a></li> <li><a href="/api/v1/base/login/">/api/v1/base/login/</a></li>
<li><a href="/api/v1/base/logout/">/api/v1/base/logout/</a></li> <li><a href="/api/v1/base/logout/">/api/v1/base/logout/</a></li>
</ul> </ul>
</div> </div>
</body> </body>
</html> </html>
{% if data.computing %} {% if data.computing %}
<div style="float:left; width:500px; margin:10px; margin-bottom:20px"> <div style="float:left; width:500px; margin:10px; margin-bottom:20px">
<table class="dashboard" style="width:100%"> <table class="dashboard" style="width:100%">
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
<td><b>Type</b></td> <td><b>Type</b></td>
<td>{{ data.computing.type }}</td> <td>{{ data.computing.type }}</td>
</tr> </tr>
<tr> <tr>
<td><b>Access mode</b></td> <td><b>Access mode</b></td>
<td>{{ data.computing.access_mode }}</td> <td>{{ data.computing.access_mode }}</td>
...@@ -73,7 +73,7 @@ ...@@ -73,7 +73,7 @@
</table> </table>
</div> </div>
{% else %} {% else %}
...@@ -95,10 +95,10 @@ ...@@ -95,10 +95,10 @@
{% if not computing_configured %} {% if not computing_configured %}
<div style="width:298px; float:left; margin:0; margin-bottom:5px; padding:2px; background: gary; color:black; text-align:center; border-bottom: #e0e0e0 solid 1px;"> <div style="width:298px; float:left; margin:0; margin-bottom:5px; padding:2px; background: gary; color:black; text-align:center; border-bottom: #e0e0e0 solid 1px;">
<i class="fa fa-info-circle"></i> Not configured <!-- style="color:#337ab7" --> <i class="fa fa-info-circle"></i> Not configured <!-- style="color:#337ab7" -->
</div> </div>
{% endif %} {% endif %}
{% if not computing_configured %} {% if not computing_configured %}
<!-- <div style="padding:10px; -webkit-filter: blur(5px); -moz-filter: blur(5px); -o-filter: blur(5px); -ms-filter: blur(5px); filter: blur(5px);"> --> <!-- <div style="padding:10px; -webkit-filter: blur(5px); -moz-filter: blur(5px); -o-filter: blur(5px); -ms-filter: blur(5px); filter: blur(5px);"> -->
...@@ -107,12 +107,12 @@ ...@@ -107,12 +107,12 @@
<div style="padding:10px;"> <div style="padding:10px;">
{% endif %} {% endif %}
{% if computing.description %} {% if computing.description %}
<div class="description-box" title="{{ computing.description }}"> <div class="description-box" title="{{ computing.description }}">
{{ computing.description }} {{ computing.description }}
</div> </div>
{% else %} {% else %}
<br/> <br/>
{% endif %} {% endif %}
<div class="image-version-box"> <div class="image-version-box">
<b>Type:</b> {{ computing.type }} <font style="font-size:0.9em">({{ computing.arch }})</font> <b>Type:</b> {{ computing.type }} <font style="font-size:0.9em">({{ computing.arch }})</font>
...@@ -133,7 +133,7 @@ ...@@ -133,7 +133,7 @@
<!-- ><b>Access:</b> {{ computing.access_mode }}<br/> -- <!-- ><b>Access:</b> {{ computing.access_mode }}<br/> --
<!-- <b>Owner:</b> {% if computing.user %}{{ computing.user }}{% else %}Platform{% endif %}<br/> --> <!-- <b>Owner:</b> {% if computing.user %}{{ computing.user }}{% else %}Platform{% endif %}<br/> -->
<!-- <b>Engines:</b> {{ computing.container_engines }} --> <!-- <b>Engines:</b> {{ computing.container_engines }} -->
<!-- {% if 'docker' in computing.container_engines %}<img src="/static/img/docker-logo.svg" style="height:18px; width:18px; margin-bottom:2px" />{% endif %} <!-- {% if 'docker' in computing.container_engines %}<img src="/static/img/docker-logo.svg" style="height:18px; width:18px; margin-bottom:2px" />{% endif %}
{% if 'singularity' in computing.container_engines %}<img src="/static/img/singularity-logo.svg" style="height:18px; width:18px; margin-bottom:2px" />{% endif %}--> {% if 'singularity' in computing.container_engines %}<img src="/static/img/singularity-logo.svg" style="height:18px; width:18px; margin-bottom:2px" />{% endif %}-->
{% if container %} {% if container %}
...@@ -152,15 +152,15 @@ ...@@ -152,15 +152,15 @@
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}
\ No newline at end of file