diff --git a/run_webapp_unit_tests.sh b/run_webapp_unit_tests.sh
index b6f78879e7c33d26897f41981332fa90ed17b4ed..051d9f4e5217c5d08b2d5759c572b8bd213fe5bf 100755
--- a/run_webapp_unit_tests.sh
+++ b/run_webapp_unit_tests.sh
@@ -6,4 +6,8 @@
 DJANGO_LOG_LEVEL="CRITICAL"
 ROSETTA_LOG_LEVEL="CRITICAL"
 
-rosetta/shell webapp "cd /opt/webapp_code && DJANGO_LOG_LEVEL=$DJANGO_LOG_LEVEL ROSETTA_LOG_LEVEL=$ROSETTA_LOG_LEVEL python3 manage.py test $@"
+# Set DB to SQLIte in-memory
+DJANGO_DB_ENGINE="django.db.backends.sqlite3"
+DJANGO_DB_NAME=":memory:"
+
+rosetta/shell webapp "export DJANGO_DB_ENGINE=$DJANGO_DB_ENGINE && export DJANGO_DB_NAME=$DJANGO_DB_NAME && cd /opt/webapp_code && python3 manage.py makemigrations && DJANGO_LOG_LEVEL=$DJANGO_LOG_LEVEL ROSETTA_LOG_LEVEL=$ROSETTA_LOG_LEVEL python3 manage.py test $@"
diff --git a/services/base/keys/id_rsa.pub b/services/base/keys/id_rsa.pub
index e4e1df61a89a45e50620f4bc74ba5547481ecd2d..9a0504b546f020c935181e7649fef5b6c32de8d0 100644
--- a/services/base/keys/id_rsa.pub
+++ b/services/base/keys/id_rsa.pub
@@ -1 +1 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC2n4wiLiRmE1sla5+w0IW3wwPW/mqhhkm7IyCBS+rGTgnts7xsWcxobvamNdD6KSLNnjFZbBb7Yaf/BvWrwQgdqIFVU3gRWHYzoU6js+lKtBjd0e2DAVGivWCKEkSGLx7zhx7uH/Jt8kyZ4NaZq0p5+SFHBzePdR/1rURd8G8+G3OaCPKqP+JQT4RMUQHC5SNRJLcK1piYdmhDiYEyuQG4FlStKCWLCXeUY2EVirNMeQIfOgbUHJsVjH07zm1y8y7lTWDMWVZOnkG6Ap5kB+n4l1eWbslOKgDv29JTFOMU+bvGvYZh70lmLK7Hg4CMpXVgvw5VF9v97YiiigLwvC7wasBHaASwH7wUqakXYhdGFxJ23xVMSLnvJn4S++4L8t8bifRIVqhT6tZCPOU4fdOvJKCRjKrf7gcW/E33ovZFgoOCJ2vBLIh9N9ME0v7tG15JpRtgIBsCXwLcl3tVyCZJ/eyYMbc3QJGsbcPGb2CYRjDbevPCQlNavcMdlyrNIke7VimM5aW8OBJKVh5wCNRpd9XylrKo1cZHYxu/c5Lr6VUZjLpxDlSz+IuTn4VE7vmgHNPnXdlxRKjLHG/FZrZTSCWFEBcRoSa/hysLSFwwDjKd9nelOZRNBvJ+NY48vA8ixVnk4WAMlR/5qhjTRam66BVysHeRcbjJ2IGjwTJC5Q== docker@dev.ops
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC2n4wiLiRmE1sla5+w0IW3wwPW/mqhhkm7IyCBS+rGTgnts7xsWcxobvamNdD6KSLNnjFZbBb7Yaf/BvWrwQgdqIFVU3gRWHYzoU6js+lKtBjd0e2DAVGivWCKEkSGLx7zhx7uH/Jt8kyZ4NaZq0p5+SFHBzePdR/1rURd8G8+G3OaCPKqP+JQT4RMUQHC5SNRJLcK1piYdmhDiYEyuQG4FlStKCWLCXeUY2EVirNMeQIfOgbUHJsVjH07zm1y8y7lTWDMWVZOnkG6Ap5kB+n4l1eWbslOKgDv29JTFOMU+bvGvYZh70lmLK7Hg4CMpXVgvw5VF9v97YiiigLwvC7wasBHaASwH7wUqakXYhdGFxJ23xVMSLnvJn4S++4L8t8bifRIVqhT6tZCPOU4fdOvJKCRjKrf7gcW/E33ovZFgoOCJ2vBLIh9N9ME0v7tG15JpRtgIBsCXwLcl3tVyCZJ/eyYMbc3QJGsbcPGb2CYRjDbevPCQlNavcMdlyrNIke7VimM5aW8OBJKVh5wCNRpd9XylrKo1cZHYxu/c5Lr6VUZjLpxDlSz+IuTn4VE7vmgHNPnXdlxRKjLHG/FZrZTSCWFEBcRoSa/hysLSFwwDjKd9nelOZRNBvJ+NY48vA8ixVnk4WAMlR/5qhjTRam66BVysHeRcbjJ2IGjwTJC5Q== rosetta@rosetta.platform
diff --git a/services/webapp/code/rosetta/base_app/fields.py b/services/webapp/code/rosetta/base_app/fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc59f97038c09247ba208fcbc4a65e1c38ab12e2
--- /dev/null
+++ b/services/webapp/code/rosetta/base_app/fields.py
@@ -0,0 +1,29 @@
+import json
+from django.db.models import Field
+
+class JSONField(Field):
+    def db_type(self, connection):
+        return 'text'
+
+    def from_db_value(self, value, expression, connection):
+        if value is not None:
+            return self.to_python(value)
+        return value
+
+    def to_python(self, value):
+        if value is not None:
+            try:
+                return json.loads(value)
+            except (TypeError, ValueError):
+                return value
+        return value
+
+    def get_prep_value(self, value):
+        if value is not None:
+            return str(json.dumps(value))
+        return value
+
+    def value_to_string(self, obj):
+        return self.value_from_object(obj)
+
+# Credits: https://medium.com/@philamersune/using-postgresql-jsonfield-in-sqlite-95ad4ad2e5f1
\ No newline at end of file
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 af7f6664adbe8e6b66cb4b5e375537ca29aaaaa2..2aba472e8f252cb1bbd161543c31fae0f5436dbc 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
@@ -1,6 +1,6 @@
 from django.core.management.base import BaseCommand
 from django.contrib.auth.models import User
-from ...models import Profile, Container, Computing
+from ...models import Profile, Container, Computing, ComputingSysConf, ComputingUserConf
 
 class Command(BaseCommand):
     help = 'Adds the admin superuser with \'a\' password.'
@@ -69,22 +69,50 @@ class Command(BaseCommand):
                                      registry      = 'docker_hub',
                                      service_ports = '8888')
 
+        # Computing resources
+        computing_resources = Computing.objects.all()
+        if computing_resources:
+            print('Not creating demo computing resources as they already exist')
+        else:
+            print('Creating demo computing resources containers...')
 
+            # Local computing resource
+            Computing.objects.create(user = None,
+                                     name = 'Local',
+                                     type = 'local')
+    
+            # Demo remote computing resource
+            demo_remote_computing = Computing.objects.create(user = None,
+                                     name = 'Demo remote',
+                                     type = 'remote',
+                                     requires_sys_conf  = True,
+                                     requires_user_conf = False)
+    
+            # Create demo remote sys computing conf
+            ComputingSysConf.objects.create(computing = demo_remote_computing,
+                                            data      = {'host': 'slurmclusterworker-one',
+                                                         'user': 'rosetta',
+                                                         'identity': 'privkey?'})
 
-        # Computing resources
-        #Computing.objects.create(user = None,
-        #                         name = 'L',
-        #                         type = '')
 
-        # Computing resources
-        #Computing.objects.create(user = None,
-        #                         name = 'L',
-        #                         type = '')
+            # Demo slurm computing resource
+            demo_slurm_computing = Computing.objects.create(user = None,
+                                     name = 'Demo Slurm',
+                                     type = 'slurm',
+                                     requires_sys_conf  = True,
+                                     requires_user_conf = True)
+    
+            # Create demo slurm sys computing conf
+            ComputingSysConf.objects.create(computing = demo_slurm_computing,
+                                            data      = {'master': 'slurmclusterworker-master'})
+
+            # Create demo slurm user computing conf
+            ComputingUserConf.objects.create(user      = testuser,
+                                             computing = demo_slurm_computing,
+                                             data      = {'user': 'testuser',
+                                                          'id_rsa': '/rosetta/.ssh/id_rsa',
+                                                          'id_rsa.pub': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC2n4wiLiRmE1sla5+w0IW3wwPW/mqhhkm7IyCBS+rGTgnts7xsWcxobvamNdD6KSLNnjFZbBb7Yaf/BvWrwQgdqIFVU3gRWHYzoU6js+lKtBjd0e2DAVGivWCKEkSGLx7zhx7uH/Jt8kyZ4NaZq0p5+SFHBzePdR/1rURd8G8+G3OaCPKqP+JQT4RMUQHC5SNRJLcK1piYdmhDiYEyuQG4FlStKCWLCXeUY2EVirNMeQIfOgbUHJsVjH07zm1y8y7lTWDMWVZOnkG6Ap5kB+n4l1eWbslOKgDv29JTFOMU+bvGvYZh70lmLK7Hg4CMpXVgvw5VF9v97YiiigLwvC7wasBHaASwH7wUqakXYhdGFxJ23xVMSLnvJn4S++4L8t8bifRIVqhT6tZCPOU4fdOvJKCRjKrf7gcW/E33ovZFgoOCJ2vBLIh9N9ME0v7tG15JpRtgIBsCXwLcl3tVyCZJ/eyYMbc3QJGsbcPGb2CYRjDbevPCQlNavcMdlyrNIke7VimM5aW8OBJKVh5wCNRpd9XylrKo1cZHYxu/c5Lr6VUZjLpxDlSz+IuTn4VE7vmgHNPnXdlxRKjLHG/FZrZTSCWFEBcRoSa/hysLSFwwDjKd9nelOZRNBvJ+NY48vA8ixVnk4WAMlR/5qhjTRam66BVysHeRcbjJ2IGjwTJC5Q== rosetta@rosetta.platform'})
 
-        # Computing resources
-        #Computing.objects.create(user = None,
-        #                         name = 'L',
-        #                         type = '')
 
 
 
diff --git a/services/webapp/code/rosetta/base_app/models.py b/services/webapp/code/rosetta/base_app/models.py
index 17a3fbc5e0f1102de38417a0da9c17edabdeeb1e..2310a1abe3bf8738f05aabda8d2f9187173b9e7b 100644
--- a/services/webapp/code/rosetta/base_app/models.py
+++ b/services/webapp/code/rosetta/base_app/models.py
@@ -1,12 +1,25 @@
 import uuid
 import enum
 
+from django.conf import settings
 from django.db import models
 from django.contrib.auth.models import User
 from django.utils import timezone
 
 from .utils import os_shell
 
+if 'sqlite' in settings.DATABASES['default']['ENGINE']:
+    from .fields import JSONField
+else:
+    from django.contrib.postgres.fields import JSONField
+
+class ConfigurationError(Exception):
+    pass
+
+class ConsistencyError(Exception):
+    pass
+
+
 # Setup logging
 import logging
 logger = logging.getLogger(__name__)
@@ -89,7 +102,11 @@ class Computing(models.Model):
     user = models.ForeignKey(User, related_name='+', on_delete=models.CASCADE, null=True)
     # If a compute resource has no user, it will be available to anyone. Can be created, edited and deleted only by admins.
     
-    name  = models.CharField('Computing Name', max_length=255, blank=False, null=False)
+    name = models.CharField('Computing Name', max_length=255, blank=False, null=False)
+    type = models.CharField('Computing Type', max_length=255, blank=False, null=False)
+
+    requires_sys_conf  = models.BooleanField(default=False)
+    requires_user_conf = models.BooleanField(default=False)
 
     def __str__(self):
         return str('Computing Resource "{}" of user "{}"'.format(self.name, self.user))
@@ -98,6 +115,81 @@ class Computing(models.Model):
     def id(self):
         return str(self.uuid).split('-')[0]
 
+    # Validate conf
+    def validate_conf_data(self, sys_conf_data=None, user_conf_data=None):
+        
+        if self.type == 'local':
+            pass
+        
+        elif self.type == 'remote':
+            # Check that we have all the data for a remote computing resource
+
+            # Look for host:
+            host_found = False
+            if sys_conf_data  and 'host' in sys_conf_data  and sys_conf_data['host']:  host_found=True
+            if user_conf_data and 'host' in user_conf_data and user_conf_data['host']: host_found=True
+            if not host_found:
+                raise ConfigurationError('Missing host in conf')
+            
+            
+            # Look for user:
+            user_found = False
+            if sys_conf_data  and 'user' in sys_conf_data  and sys_conf_data['user']:  user_found=True
+            if user_conf_data and 'user' in user_conf_data and user_conf_data['user']: user_found=True
+            if not user_found:
+                raise ConfigurationError('Missing user in conf')               
+
+            # Look for password/identity:
+            password_found = False
+            identity_found = False
+            if sys_conf_data  and 'password' in sys_conf_data  and sys_conf_data['password']:  password_found=True
+            if user_conf_data and 'password' in user_conf_data and user_conf_data['password']: password_found=True
+            if sys_conf_data  and 'identity' in sys_conf_data  and sys_conf_data['identity']:  identity_found=True
+            if user_conf_data and 'identity' in user_conf_data and user_conf_data['identity']: identity_found=True       
+            if not password_found and not identity_found:
+                raise ConfigurationError('Missing password or identity in conf')
+
+        elif self.type == 'slurm':
+            raise NotImplementedError('Not yet implemented for Slurm')
+
+        else:
+            raise ConsistencyError('Unknown computing type "{}"'.format(self.type))
+    
+    @property    
+    def sys_conf_data(self):          
+        return ComputingSysConf.objects.get(computing=self).data
+    
+    #@property    
+    #def user_conf_data(self):
+    #    return {'testuser':'ciao'}
+    
+    def attach_user_conf_data(self, user):
+        try:
+            self.user_conf_data = ComputingUserConf.objects.get(computing=self).data
+        except ComputingUserConf.DoesNotExist:
+            self.user_conf_data = None
+
+
+class ComputingSysConf(models.Model):
+    uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
+    computing = models.ForeignKey(Computing, related_name='+', on_delete=models.CASCADE)
+    data = JSONField(blank=True, null=True)
+
+    @property
+    def id(self):
+        return str(self.uuid).split('-')[0]
+
+
+class ComputingUserConf(models.Model):
+    uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
+    user = models.ForeignKey(User, related_name='+', on_delete=models.CASCADE, null=True)
+    computing = models.ForeignKey(Computing, related_name='+', on_delete=models.CASCADE)
+    data = JSONField(blank=True, null=True)
+
+    @property
+    def id(self):
+        return str(self.uuid).split('-')[0]
+
 
 #=========================
 #  Tasks 
@@ -109,14 +201,14 @@ class Task(models.Model):
     name      = models.CharField('Task name', max_length=36, blank=False, null=False)
     status    = models.CharField('Task status', max_length=36, blank=True, null=True)
     created   = models.DateTimeField('Created on', default=timezone.now)
-    computing = models.ForeignKey(Computing, related_name='+', on_delete=models.CASCADE)
     pid       = models.IntegerField('Task pid', blank=True, null=True)
     port      = models.IntegerField('Task port', blank=True, null=True)
     ip        = models.CharField('Task ip address', max_length=36, blank=True, null=True)
     tunnel_port = models.IntegerField('Task tunnel port', blank=True, null=True)
 
     # Links
-    container    = models.ForeignKey('Container', on_delete=models.CASCADE, related_name='+')
+    computing = models.ForeignKey(Computing, related_name='+', on_delete=models.CASCADE)
+    container = models.ForeignKey('Container', on_delete=models.CASCADE, related_name='+')
 
     def save(self, *args, **kwargs):
         
diff --git a/services/webapp/code/rosetta/base_app/templates/add_container.html b/services/webapp/code/rosetta/base_app/templates/add_container.html
index 8f9af73bfcbffe4b94c35b588b8d3208b0217a81..e231d99c5c408188732332e00f282a3edb93e6fe 100644
--- a/services/webapp/code/rosetta/base_app/templates/add_container.html
+++ b/services/webapp/code/rosetta/base_app/templates/add_container.html
@@ -49,9 +49,9 @@
            </tr>
 
            <tr>
-            <td><b>Service port(s)</b></td>
+            <td><b>Service port</b></td>
             <td>
-             <input type="text" name="container_service_ports" value="" placeholder="" size="5" required />
+             <input type="text" name="container_service_port" value="" placeholder="" size="5" />
             </td>
            </tr>
 
diff --git a/services/webapp/code/rosetta/base_app/templates/components/computing.html b/services/webapp/code/rosetta/base_app/templates/components/computing.html
new file mode 100644
index 0000000000000000000000000000000000000000..ad512797282b1ef3004bfff875aa02953f895209
--- /dev/null
+++ b/services/webapp/code/rosetta/base_app/templates/components/computing.html
@@ -0,0 +1,41 @@
+      <table class="dashboard">
+
+       <tr>
+        <td><b>ID</b></td>
+        <td><a href="?uuid={{ computing.uuid }}">{{ computing.id }}</a></td>
+       </tr>
+
+       <tr>
+        <td><b>Name</b></td>
+        <td>{{ computing.name }}</td>
+       </tr>
+
+       <tr>
+        <td><b>Type</b></td>
+        <td>{{ computing.type }}</td>
+       </tr>
+
+       <tr>
+        <td><b>Owner</b></td>
+        <td>{% if computing.user %}{{ computing.user }}{% else %}Platform{% endif %}</td>
+       </tr>
+
+       <tr>
+        <td><b>Sys Conf</b></td>
+        <td>{{ computing.sys_conf_data }}</td>
+       </tr>
+
+       <tr>
+        <td><b>User Conf</b></td>
+        <td>{{ computing.user_conf_data }}</td>
+       </tr>
+
+       
+       {% if computing.user %}
+       <tr>
+        <td><b>Operations</b></td>
+        <td><a href="?action=delete&uuid={{ computing.uuid }}">Delete</a></td>
+       </tr>
+       {% endif %}
+      </table>
+      <br/>
\ No newline at end of file
diff --git a/services/webapp/code/rosetta/base_app/templates/computings.html b/services/webapp/code/rosetta/base_app/templates/computings.html
index 5ae68940183e6c832939a5f672a0c4260f41004d..26982d46d0aca41620460ea4734b8f62ccb74674 100644
--- a/services/webapp/code/rosetta/base_app/templates/computings.html
+++ b/services/webapp/code/rosetta/base_app/templates/computings.html
@@ -10,15 +10,15 @@
     <div class="span8 offset2">
       
       {% if data.computing %}
-      <h1><a href="/computings">Computing Resources List</a> &gt; {{ data.computing.id }}  </h1>      
+      <h1><a href="/computings">Computing List</a> &gt; {{ data.computing.id }}  </h1>      
       {% else %}
-      <h1>Computing Resources List</h1>
+      <h1>Computing List</h1>
       {% endif %}
       
       <hr/>
 
       {% if data.computing %}
-      {% include "components/computing.html" with computing=data.computing %}
+      {% include "components/computing.html" with computing=data.computin %}
       {% else %}
       
       {% for computing in data.platform_computings %}
diff --git a/services/webapp/code/rosetta/base_app/templates/create_task.html b/services/webapp/code/rosetta/base_app/templates/create_task.html
index e6c1ed63459b531eec7ff83295dbc29072233656..87d4a8d1138fff9cf78c134fda9f7a9bf72ac014 100644
--- a/services/webapp/code/rosetta/base_app/templates/create_task.html
+++ b/services/webapp/code/rosetta/base_app/templates/create_task.html
@@ -11,14 +11,16 @@
       <h1>New Task</h1> 
       <hr>
 
-      {% if not data.created %}
+      {% if data.step == 'one' %}
 
-          <h3>Choose a name and a type for your new Task.</h3> 
+          <h3>Step 1: name, container and computing.</h3> 
           
           <br/>
           
           <form action="/create_task/" method="POST">
           {% csrf_token %}
+          <input type="hidden" name="step" value="one" />
+
 
           <table class="dashboard" style="max-width:700px">
     
@@ -29,22 +31,63 @@
             </td>
            </tr>
 
-           <!--
-           <tr>
-            <td><b>Task user</b></td>
-            <td>
-             <input type="text" name="name" value="" placeholder="metauser" size="23" disabled />
+          <tr>
+            <td><b>Task container</b></td><td>
+              <select name="task_container_uuid" >
+              <!-- <option value="metadesktop" selected>Meta Desktop</option>
+              <option value="astroccok">Astrocook</option>
+              <option value="gadgetviewer">Gadget Viewer</option> -->
+              {% for container in data.platform_containers %}
+              <option value="{{container.uuid}}">{{container.image}} ({{container.type}})</option> -->
+              {% endfor %}
+              {% for container in data.user_containers %}
+              <option value="{{container.uuid}}">{{container.image}} ({{container.type}})</option> -->
+              {% endfor %}              
+              
+              </select>
+              &nbsp; | <a href="/add_container">Add new...</a>          
             </td>
-           </tr>-->
+           </tr>
            
-           <!--<tr>
-            <td><b>Task password</b></td>
-            <td>
-             <input type="password" name="password" value="" placeholder="" size="23" disabled />
+           <tr>
+            <td><b>Computing resource</b></td><td>
+              <select name="task_computing" >
+              <option value="local" selected>Local</option>
+              <option value="demoremote">Demo remote</option>
+              <option value="demoslurm">Demo Slurm cluster</option>
+              </select>
+              &nbsp; | <a href="/add_computing">Add new...</a>
             </td>
-           </tr> -->
+           </tr>
+
+           <tr>
+           <td colspan=2 align=center style="padding:20px">
+           <input type="submit" value="Next">
+           </td>
+           </tr>
+          </table>
+          </form>
+
+      {% elif data.step == 'two' %}
+
+          <h3>Choose a name and a type for your new Task.</h3> 
+          
+          <br/>
+          
+          <form action="/create_task/" method="POST">
+          {% csrf_token %}
+          <input type="hidden" name="step" value="two" />
 
+          <table class="dashboard" style="max-width:700px">
+    
            <tr>
+            <td><b>Task name </b></td>
+            <td>
+             <input type="text" name="task_name" value="" placeholder="" size="23" required />
+            </td>
+           </tr>
+
+          <tr>
             <td><b>Task container</b></td><td>
               <select name="task_container_uuid" >
               <!-- <option value="metadesktop" selected>Meta Desktop</option>
@@ -73,6 +116,30 @@
             </td>
            </tr>
 
+           <tr>
+            <td><b>Task user</b></td>
+            <td>
+             <input type="text" name="name" value="" placeholder="metauser" size="23" disabled />
+            </td>
+           </tr>
+           
+           <tr>
+            <td><b>Task password</b></td>
+            <td>
+             <input type="password" name="password" value="" placeholder="" size="23" disabled />
+            </td>
+           </tr>
+
+           <tr>
+            <td><b>Access method</b></td><td>
+              <select name="access_method" >
+              <option value="http_proxy" selected>HTTP proxy</option>
+              <option value="direct_tunnel">Direct tunnel</option>
+              <option value="None">None</option>
+              </select>
+            </td>
+           </tr>
+
            <!-- <tr>
             <td><b>Run using</b></td><td>
               <select name="run_using" >
@@ -97,20 +164,7 @@
           </table>
           </form>
 
-          <br/>          
-          <br/>
-          <br/>
-          <br/>
-          <br/>
-          <br/>
-          <br/>
-          <br/>
-          <br/>
-          
-
-
 
- 
       {% else %}
         Ok, task created. Go back to your <a href="/tasks">task list</a>.
         
diff --git a/services/webapp/code/rosetta/base_app/tests/test_models.py b/services/webapp/code/rosetta/base_app/tests/test_models.py
new file mode 100644
index 0000000000000000000000000000000000000000..29d62ee9b49d286adc2bfd4f33c73a96e7030622
--- /dev/null
+++ b/services/webapp/code/rosetta/base_app/tests/test_models.py
@@ -0,0 +1,47 @@
+import json
+
+from django.contrib.auth.models import User
+        
+from .common import BaseAPITestCase
+from ..models import Profile, Computing, ComputingSysConf
+
+class Modeltest(BaseAPITestCase):
+
+    def setUp(self):
+        
+        # Create test users
+        self.user = User.objects.create_user('testuser', password='testpass')
+        self.anotheruser = User.objects.create_user('anotheruser', password='anotherpass')
+
+        # Create test profile
+        Profile.objects.create(user=self.user, authtoken='ync719tce917tec197t29cn712eg')
+
+
+    def test_computing(self):
+        '''Test Computing and their Conf models''' 
+         
+        computing = Computing.objects.create(name='MyComp', type='remote')
+        
+        computingSysConf = ComputingSysConf.objects.create(computing=computing, data={'myvar':42})
+        
+        self.assertEqual(ComputingSysConf.objects.all()[0].data, {'myvar':42})
+        
+        # Will raise, no host or user or pass/identity
+        with self.assertRaises(Exception):
+            computing.validate_conf_data(sys_conf_data=computingSysConf.data)
+
+        # Complete conf
+        computingSysConf_1 = ComputingSysConf.objects.create(computing=computing, data={'host':'localhost', 'user':'testuser', 'password':'testpass'})
+        
+        # Will not raise
+        computing.validate_conf_data(sys_conf_data=computingSysConf_1.data)
+
+
+        # Complete conf
+        #computingSysConf_1 = ComputingSysConf.objects.create(computing=computing, data={'host':'localhost', 'user':'testuser', 'password':'testpass'})
+        
+        # Will not raise
+        #computing.validate_conf_data(sys_conf_data=computingSysConf_1.data)
+
+
+
diff --git a/services/webapp/code/rosetta/base_app/views.py b/services/webapp/code/rosetta/base_app/views.py
index 85f051829536728f6186ae7956bfdc863a825cf8..1766838fee93548b652636de1fd0e85dd16b393e 100644
--- a/services/webapp/code/rosetta/base_app/views.py
+++ b/services/webapp/code/rosetta/base_app/views.py
@@ -37,6 +37,9 @@ UNSUPPORTED_TYPES_VS_REGISTRIES = ['docker:singularity_hub']
 
 TASK_DATA_DIR = "/data"
 
+# Task cache
+_task_cache = {}
+
 #=========================
 #  Decorators
 #=========================
@@ -166,6 +169,145 @@ def private_view(wrapped_view):
 
 
 
+#------------------------------------------------------
+#   Helper functions
+#------------------------------------------------------
+
+def start_task(task):
+
+    if task.computing == 'local':
+
+        # Get our ip address
+        #import netifaces
+        #netifaces.ifaddresses('eth0')
+        #backend_ip = netifaces.ifaddresses('eth0')[netifaces.AF_INET][0]['addr']
+
+        # Init run command #--cap-add=NET_ADMIN --cap-add=NET_RAW
+        run_command  = 'sudo docker run  --network=rosetta_default --name rosetta-task-{}'.format( task.id)
+
+        # Data volume
+        run_command += ' -v {}/task-{}:/data'.format(TASK_DATA_DIR, task.id)
+
+        # Set registry string
+        if task.container.registry == 'local':
+            registry_string = 'localhost:5000/'
+        else:
+            registry_string  = ''
+
+        # Host name, image entry command
+        run_command += ' -h task-{} -d -t {}{}'.format(task.id, registry_string, task.container.image)
+
+        # Run the task Debug
+        logger.debug('Running new task with command="{}"'.format(run_command))
+        out = os_shell(run_command, capture=True)
+        if out.exit_code != 0:
+            raise Exception(out.stderr)
+        else:
+            task_tid = out.stdout
+            logger.debug('Created task with id: "{}"'.format(task_tid))
+
+
+            # Get task IP address
+            out = os_shell('sudo docker inspect --format \'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}\' ' + task_tid + ' | tail -n1', capture=True)
+            if out.exit_code != 0:
+                raise Exception('Error: ' + out.stderr)
+            task_ip = out.stdout
+
+            # Set fields
+            task.tid    = task_tid
+            task.status = TaskStatuses.running
+            task.ip     = task_ip
+            task.port   = int(task.container.service_ports.split(',')[0])
+
+            # Save
+            task.save()
+
+    elif task.computing == 'demoremote':
+        logger.debug('Using Demo Remote as computing resource')
+
+
+        # 1) Run the singularity container on slurmclusterworker-one (non blocking)
+        run_command = 'ssh -4 -o StrictHostKeyChecking=no slurmclusterworker-one  "export SINGULARITY_NOHTTPS=true && exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv docker://dregistry:5000/rosetta/metadesktop &> /tmp/{}.log & echo \$!"'.format(task.uuid)
+        out = os_shell(run_command, capture=True)
+        if out.exit_code != 0:
+            raise Exception(out.stderr)
+
+        # Save pid echoed by the command above
+        task_pid = out.stdout
+
+        # 2) Simulate the agent (i.e. report container IP and port port)
+
+        # Get task IP address
+        out = os_shell('sudo docker inspect --format \'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}\' slurmclusterworker-one | tail -n1', capture=True)
+        if out.exit_code != 0:
+            raise Exception('Error: ' + out.stderr)
+        task_ip = out.stdout
+
+        # Set fields
+        task.tid    = task.uuid
+        task.status = TaskStatuses.running
+        task.ip     = task_ip
+        task.pid    = task_pid
+        task.port   = int(task.container.service_ports.split(',')[0])
+
+        # Save
+        task.save()
+
+
+    else:
+        raise Exception('Consistency exception: invalid computing resource "{}'.format(task.computing))
+
+
+def stop_task(task):
+
+    if task.computing == 'local':
+    
+        # Delete the Docker container
+        standby_supported = False
+        if standby_supported:
+            stop_command = 'sudo docker stop {}'.format(task.tid)
+        else:
+            stop_command = 'sudo docker stop {} && sudo docker rm {}'.format(task.tid,task.tid)
+    
+        out = os_shell(stop_command, capture=True)
+        if out.exit_code != 0:
+            raise Exception(out.stderr)
+    
+    elif task.computing == 'demoremote':
+    
+        # Stop the task remotely
+        stop_command = 'ssh -4 -o StrictHostKeyChecking=no slurmclusterworker-one  "kill -9 {}"'.format(task.pid)
+        logger.debug(stop_command)
+        out = os_shell(stop_command, capture=True)
+        if out.exit_code != 0:
+            if not 'No such process' in out.stderr:
+                raise Exception(out.stderr)
+    
+    raise Exception('Don\'t know how to stop tasks on "{}" computing resource.'.format(task.computing))
+    
+    # Ok, save status as deleted
+    task.status = 'stopped'
+    task.save()
+    
+    # Check if the tunnel is active and if so kill it
+    logger.debug('Checking if task "{}" has a running tunnel'.format(task.tid))
+    check_command = 'ps -ef | grep ":'+str(task.tunnel_port)+':'+str(task.ip)+':'+str(task.port)+'" | grep -v grep | awk \'{print $2}\''
+    logger.debug(check_command)
+    out = os_shell(check_command, capture=True)
+    logger.debug(out)
+    if out.exit_code == 0:
+        logger.debug('Task "{}" has a running tunnel, killing it'.format(task.tid))
+        tunnel_pid = out.stdout
+        # Kill Tunnel command
+        kill_tunnel_command= 'kill -9 {}'.format(tunnel_pid)
+    
+        # Log
+        logger.debug('Killing tunnel with command: {}'.format(kill_tunnel_command))
+    
+        # Execute
+        os_shell(kill_tunnel_command, capture=True)
+        if out.exit_code != 0:
+            raise Exception(out.stderr)
 
 @public_view
 def login_view(request):
@@ -425,63 +567,9 @@ def tasks(request):
                     logger.error('Error in deleting task with uuid="{}": "{}"'.format(uuid, e))
                     return render(request, 'error.html', {'data': data})
     
-            elif action=='stop': # or delete,a and if delete also remove object
-                try:
-                    if task.computing == 'local':
-     
-                        # Delete the Docker container
-                        if standby_supported:
-                            stop_command = 'sudo docker stop {}'.format(task.tid)
-                        else:
-                            stop_command = 'sudo docker stop {} && sudo docker rm {}'.format(task.tid,task.tid)
-     
-                        out = os_shell(stop_command, capture=True)
-                        if out.exit_code != 0:
-                            raise Exception(out.stderr)
-     
-                    elif task.computing == 'demoremote':
-     
-                        # Stop the task remotely
-                        stop_command = 'ssh -4 -o StrictHostKeyChecking=no slurmclusterworker-one  "kill -9 {}"'.format(task.pid)
-                        logger.debug(stop_command)
-                        out = os_shell(stop_command, capture=True)
-                        if out.exit_code != 0:
-                            if not 'No such process' in out.stderr:
-                                raise Exception(out.stderr)
-     
-                    else:
-                        data['error']= 'Don\'t know how to stop tasks on "{}" computing resource.'.format(task.computing)
-                        return render(request, 'error.html', {'data': data})
-     
-                    # Ok, save status as deleted
-                    task.status = 'stopped'
-                    task.save()
-     
-                    # Check if the tunnel is active and if so kill it
-                    logger.debug('Checking if task "{}" has a running tunnel'.format(task.tid))
-                    check_command = 'ps -ef | grep ":'+str(task.tunnel_port)+':'+str(task.ip)+':'+str(task.port)+'" | grep -v grep | awk \'{print $2}\''
-                    logger.debug(check_command)
-                    out = os_shell(check_command, capture=True)
-                    logger.debug(out)
-                    if out.exit_code == 0:
-                        logger.debug('Task "{}" has a running tunnel, killing it'.format(task.tid))
-                        tunnel_pid = out.stdout
-                        # Kill Tunnel command
-                        kill_tunnel_command= 'kill -9 {}'.format(tunnel_pid)
-     
-                        # Log
-                        logger.debug('Killing tunnel with command: {}'.format(kill_tunnel_command))
-     
-                        # Execute
-                        os_shell(kill_tunnel_command, capture=True)
-                        if out.exit_code != 0:
-                            raise Exception(out.stderr)
-    
-                except Exception as e:
-                    data['error'] = 'Error in stopping the task'
-                    logger.error('Error in stopping task with uuid="{}": "{}"'.format(uuid, e))
-                    return render(request, 'error.html', {'data': data})
-    
+            elif action=='stop': # or delete,a and if delete also remove object               
+                stop_task(task)
+
             elif action=='connect':
     
                 # Create task tunnel
@@ -584,10 +672,16 @@ def create_task(request):
     data['user_containers'] = Container.objects.filter(user=request.user)
     data['platform_containers'] = Container.objects.filter(user=None)
 
-    # Task name if any
-    task_name = request.POST.get('task_name', None)
+    data['computing'] = Computing.objects.filter(user=None)
+
+
+    # Step if any
+    step = request.POST.get('step', None)
 
-    if task_name:
+    if step == 'one':
+
+        # We have a step one submitted, get the first tab parameters
+        task_name = request.POST.get('task_name', None)
 
         # Task container
         task_container_uuid = request.POST.get('task_container_uuid', None)
@@ -607,109 +701,45 @@ def create_task(request):
             raise ErrorMessage('Unknown computing resource "{}')
 
         # Generate the task uuid
-        str_uuid = str(uuid.uuid4())
-        str_shortuuid = str_uuid.split('-')[0]
+        task_uuid = str(uuid.uuid4())
 
         # Create the task object
-        task = Task.objects.create(uuid      = str_uuid,
-                                   user      = request.user,
-                                   name      = task_name,
-                                   status    = TaskStatuses.created,
-                                   container = task_container,
-                                   computing = task_computing)
-
+        task = Task(uuid      = task_uuid,
+                    user      = request.user,
+                    name      = task_name,
+                    status    = TaskStatuses.created,
+                    container = task_container,
+                    computing = task_computing)
 
-        # Actually start tasks
-        try:
-            if task_computing == 'local':
-
-                # Get our ip address
-                #import netifaces
-                #netifaces.ifaddresses('eth0')
-                #backend_ip = netifaces.ifaddresses('eth0')[netifaces.AF_INET][0]['addr']
+        # Save the task in the cache
+        _task_cache[task_uuid] = task
 
-                # Init run command #--cap-add=NET_ADMIN --cap-add=NET_RAW
-                run_command  = 'sudo docker run  --network=rosetta_default --name rosetta-task-{}'.format( str_shortuuid)
+        # Set step
+        data['step'] = 'two'
+        
+    elif step == 'two':
+        
+        # Get back the task
+        task_uuid = request.POST.get('task_uuid', None)
+        task = _task_cache[task_uuid]
 
-                # Data volume
-                run_command += ' -v {}/task-{}:/data'.format(TASK_DATA_DIR, str_shortuuid)
+        
+        # Add auth and/or computing parameters to the task if any
 
-                # Set registry string
-                if task.container.registry == 'local':
-                    registry_string = 'localhost:5000/'
-                else:
-                    registry_string  = ''
+        # Save the task in the DB
 
-                # Host name, image entry command
-                run_command += ' -h task-{} -d -t {}{}'.format(str_shortuuid, registry_string, task.container.image)
-
-                # Run the task Debug
-                logger.debug('Running new task with command="{}"'.format(run_command))
-                out = os_shell(run_command, capture=True)
-                if out.exit_code != 0:
-                    raise Exception(out.stderr)
-                else:
-                    task_tid = out.stdout
-                    logger.debug('Created task with id: "{}"'.format(task_tid))
+        # Start the task
+        #start_task(task)
 
+        # Set step        
+        data['step'] = 'created'
 
-                    # Get task IP address
-                    out = os_shell('sudo docker inspect --format \'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}\' ' + task_tid + ' | tail -n1', capture=True)
-                    if out.exit_code != 0:
-                        raise Exception('Error: ' + out.stderr)
-                    task_ip = out.stdout
-
-                    # Set fields
-                    task.tid    = task_tid
-                    task.status = TaskStatuses.running
-                    task.ip     = task_ip
-                    task.port   = int(task.container.service_ports.split(',')[0])
-
-                    # Save
-                    task.save()
-
-            elif task_computing == 'demoremote':
-                logger.debug('Using Demo Remote as computing resource')
-
-
-                # 1) Run the singularity container on slurmclusterworker-one (non blocking)
-                run_command = 'ssh -4 -o StrictHostKeyChecking=no slurmclusterworker-one  "export SINGULARITY_NOHTTPS=true && exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv docker://dregistry:5000/rosetta/metadesktop &> /tmp/{}.log & echo \$!"'.format(task.uuid)
-                out = os_shell(run_command, capture=True)
-                if out.exit_code != 0:
-                    raise Exception(out.stderr)
-
-                # Save pid echoed by the command above
-                task_pid = out.stdout
-
-                # 2) Simulate the agent (i.e. report container IP and port port)
-
-                # Get task IP address
-                out = os_shell('sudo docker inspect --format \'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}\' slurmclusterworker-one | tail -n1', capture=True)
-                if out.exit_code != 0:
-                    raise Exception('Error: ' + out.stderr)
-                task_ip = out.stdout
-
-                # Set fields
-                task.tid    = task.uuid
-                task.status = TaskStatuses.running
-                task.ip     = task_ip
-                task.pid    = task_pid
-                task.port   = int(task.container.service_ports.split(',')[0])
-
-                # Save
-                task.save()
-
-
-            else:
-                raise Exception('Consistency exception: invalid computing resource "{}'.format(task_computing))
-
-        except Exception as e:
-            data['error'] = 'Error in creating new Task.'
-            logger.error(e)
-            return render(request, 'error.html', {'data': data})
+    else:
+        
+        # Set step
+        data['step'] = 'one'
+        
 
-        # Set created switch
-        data['created'] = True
 
     return render(request, 'create_task.html', {'data': data})
 
@@ -873,14 +903,15 @@ def add_container(request):
         if container_type+':'+container_registry in UNSUPPORTED_TYPES_VS_REGISTRIES:
             raise ErrorMessage('Sorry, container type "{}" is not compatible with registry type "{}"'.format(container_type, container_registry))
 
-        # Container service ports
+        # Container service ports. TODO: support multiple ports? 
         container_service_ports = request.POST.get('container_service_ports', None)
 
-        try:
-            for container_service_port in container_service_ports:
-                int(container_service_port)
-        except:
-            raise ErrorMessage('Invalid service port "{}"'.format(container_service_port))
+        if container_service_ports:       
+            try:
+                for container_service_port in container_service_ports.split(','):
+                    int(container_service_port)
+            except:
+                raise ErrorMessage('Invalid service port(s) in "{}"'.format(container_service_ports))
 
         # Log
         logger.debug('Creating new container object with image="{}", type="{}", registry="{}", service_ports="{}"'.format(container_image, container_type, container_registry, container_service_ports))
@@ -912,7 +943,11 @@ def computings(request):
     data['title']   = 'Add computing'
     data['name']    = request.POST.get('name',None)
     
-    data['computings'] = Computing.objects.all()
+    data['platform_computings'] = Computing.objects.filter(user=None)
+    
+    # Attach user conf in any
+    for platform_computing in data['platform_computings']:
+        platform_computing.attach_user_conf_data(request.user)
 
     return render(request, 'computings.html', {'data': data})
 
diff --git a/services/webapp/prestartup_webapp.sh b/services/webapp/prestartup_webapp.sh
index befdd9ac85231f6e79973421843ea6c12774b324..7730e0dc9517a229bb431395d9cb5bebeacd085c 100644
--- a/services/webapp/prestartup_webapp.sh
+++ b/services/webapp/prestartup_webapp.sh
@@ -8,36 +8,40 @@ chown rosetta:rosetta /var/log/webapp
 mkdir -p /data/resources 
 chown rosetta:rosetta /data/resources
 
+
 #-----------------------------
 # Set migrations data folder
 #-----------------------------
 
-if [[ "x$(mount | grep /devmigrations)" == "x" ]] ; then
-    # If the migrations folder is not mounted (not a Docker volume), use the /data directory via links to use data persistency
+if [[ "xDJANGO_DB_NAME" == "x:memory:" ]] ; then
+    # Use the /tmp directory via links to use ephemeral data
+    mkdir -p /tmp/migrations
+    $MIGRATIONS_DATA_FOLDER=/tmp/migrations
+    echo "Using temporary migrations in $MIGRATIONS_DATA_FOLDER"
+else
+    # Use the /data directory via links to use data persistency
     MIGRATIONS_DATA_FOLDER="/data/migrations"
     # Also if the migrations folder in /data does not exist, create it now
     mkdir -p /data/migrations
-else
-    # If the migrations folder is mounted (a Docker volume), use it as we are in dev mode
-    MIGRATIONS_DATA_FOLDER="/devmigrations"
+	echo "Persisting migrations in $MIGRATIONS_DATA_FOLDER"
 fi
-echo "Persisting migrations in $MIGRATIONS_DATA_FOLDER"
 
 
 #-----------------------------
 # Handle Base App migrations
 #-----------------------------
-
+	
 # Remove potential leftovers
 rm -f /opt/webapp_code/rosetta/base_app/migrations
+
+# If migrations were not already initialized, do it now
 if [ ! -d "$MIGRATIONS_DATA_FOLDER/base_app" ] ; then
-    # If migrations were not already initialized, do it now
     echo "Initializing migrations for base_app"...
     mkdir $MIGRATIONS_DATA_FOLDER/base_app && chown rosetta:rosetta $MIGRATIONS_DATA_FOLDER/base_app
     touch $MIGRATIONS_DATA_FOLDER/base_app/__init__.py && chown rosetta:rosetta $MIGRATIONS_DATA_FOLDER/base_app/__init__.py
 fi
 
-# Use the persisted migrations
+# Use the right migrations folder
 ln -s $MIGRATIONS_DATA_FOLDER/base_app /opt/webapp_code/rosetta/base_app/migrations