From be1e382ecd918bf2734adc07fe55518009bb3de3 Mon Sep 17 00:00:00 2001
From: Romain Courteaud <romain@nexedi.com>
Date: Tue, 24 May 2022 14:52:20 +0000
Subject: [PATCH] slapos_slap_tool: support for the virtual master logic

A project reference is required to create a compute node.

Add compatibility with the user request
Console client does not send a project reference.
Try to guess it for simple cases
---
 .../extension.erp5.SlapOSSlapTool.py          |   4 +-
 .../extension.erp5.SlapOSSlapTool.xml         |  27 +-
 .../test.erp5.testSlapOSSlapTool.py           | 278 ++++++++++--------
 ...SlapOSSlapToolComputeNodeUpdateFromDict.py |   4 +-
 .../portal_components/tool.erp5.SlapTool.py   |  87 ++++--
 5 files changed, 233 insertions(+), 167 deletions(-)

diff --git a/master/bt5/slapos_slap_tool/ExtensionTemplateItem/portal_components/extension.erp5.SlapOSSlapTool.py b/master/bt5/slapos_slap_tool/ExtensionTemplateItem/portal_components/extension.erp5.SlapOSSlapTool.py
index d3df777a7..600afc06e 100644
--- a/master/bt5/slapos_slap_tool/ExtensionTemplateItem/portal_components/extension.erp5.SlapOSSlapTool.py
+++ b/master/bt5/slapos_slap_tool/ExtensionTemplateItem/portal_components/extension.erp5.SlapOSSlapTool.py
@@ -16,7 +16,9 @@ def getComputeNodeReferenceAndUserId(item):
     if partition is not None:
       compute_node = partition.getParentValue()
 
-  if compute_node is not None and compute_node.getValidationState() == 'validated':
+  if (compute_node is not None) and \
+    (compute_node.getPortalType() == 'Compute Node') and \
+    (compute_node.getValidationState() == 'validated'):
     return compute_node, compute_node.getReference(), compute_node.getUserId()
   return None, None, None
 
diff --git a/master/bt5/slapos_slap_tool/ExtensionTemplateItem/portal_components/extension.erp5.SlapOSSlapTool.xml b/master/bt5/slapos_slap_tool/ExtensionTemplateItem/portal_components/extension.erp5.SlapOSSlapTool.xml
index e2809f6ea..cc099b160 100644
--- a/master/bt5/slapos_slap_tool/ExtensionTemplateItem/portal_components/extension.erp5.SlapOSSlapTool.xml
+++ b/master/bt5/slapos_slap_tool/ExtensionTemplateItem/portal_components/extension.erp5.SlapOSSlapTool.xml
@@ -6,12 +6,6 @@
     </pickle>
     <pickle>
       <dictionary>
-        <item>
-            <key> <string>_recorded_property_dict</string> </key>
-            <value>
-              <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
-            </value>
-        </item>
         <item>
             <key> <string>default_reference</string> </key>
             <value> <string>SlapOSSlapTool</string> </value>
@@ -55,28 +49,13 @@
         <item>
             <key> <string>workflow_history</string> </key>
             <value>
-              <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
+              <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
             </value>
         </item>
       </dictionary>
     </pickle>
   </record>
   <record id="2" aka="AAAAAAAAAAI=">
-    <pickle>
-      <global name="PersistentMapping" module="Persistence.mapping"/>
-    </pickle>
-    <pickle>
-      <dictionary>
-        <item>
-            <key> <string>data</string> </key>
-            <value>
-              <dictionary/>
-            </value>
-        </item>
-      </dictionary>
-    </pickle>
-  </record>
-  <record id="3" aka="AAAAAAAAAAM=">
     <pickle>
       <global name="PersistentMapping" module="Persistence.mapping"/>
     </pickle>
@@ -89,7 +68,7 @@
                 <item>
                     <key> <string>component_validation_workflow</string> </key>
                     <value>
-                      <persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
+                      <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
                     </value>
                 </item>
               </dictionary>
@@ -98,7 +77,7 @@
       </dictionary>
     </pickle>
   </record>
-  <record id="4" aka="AAAAAAAAAAQ=">
+  <record id="3" aka="AAAAAAAAAAM=">
     <pickle>
       <global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
     </pickle>
diff --git a/master/bt5/slapos_slap_tool/TestTemplateItem/portal_components/test.erp5.testSlapOSSlapTool.py b/master/bt5/slapos_slap_tool/TestTemplateItem/portal_components/test.erp5.testSlapOSSlapTool.py
index 3ddb6a418..38164aed8 100644
--- a/master/bt5/slapos_slap_tool/TestTemplateItem/portal_components/test.erp5.testSlapOSSlapTool.py
+++ b/master/bt5/slapos_slap_tool/TestTemplateItem/portal_components/test.erp5.testSlapOSSlapTool.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 # Copyright (c) 2002-2012 Nexedi SA and Contributors. All Rights Reserved.
-from erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixin
+from erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixin, TemporaryAlarmScript
 
 from DateTime import DateTime
 from App.Common import rfc1123_date
@@ -53,17 +53,16 @@ class TestSlapOSSlapToolMixin(SlapOSTestCaseMixin):
     SlapOSTestCaseMixin.afterSetUp(self)
     self.portal_slap = self.portal.portal_slap
 
+    self.project = self.addProject()
+
     # Prepare compute_node
-    self.compute_node = self.portal.compute_node_module.template_compute_node\
-        .Base_createCloneDocument(batch_mode=1)
+    self.compute_node = self.portal.compute_node_module\
+        .newContent(portal_type="Compute Node")
     self.compute_node.edit(
       title="Compute Node %s" % self.new_id,
-      reference="TESTCOMP-%s" % self.new_id
+      reference="TESTCOMP-%s" % self.new_id,
+      follow_up_value=self.project
     )
-    if getattr(self, "person", None) is not None:
-      self.compute_node.edit(
-        source_administration_value=getattr(self, "person", None),
-        )
     self.compute_node.validate()
     self._addCertificateLogin(self.compute_node)
 
@@ -83,7 +82,7 @@ class TestSlapOSSlapToolMixin(SlapOSTestCaseMixin):
 
 class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
   def test_activate_getFullComputerInformation_first_access(self):
-    self._makeComplexComputeNode(with_slave=True)
+    self._makeComplexComputeNode(self.project, with_slave=True)
     self.portal.REQUEST['disable_isTestRun'] = True
 
     self.login(self.compute_node_user_id)
@@ -202,7 +201,11 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
 
     # Remove the slave link to the partition
     # Compute Node should loose permission to access the slave instance
+    self.logout()
+    self.login()
     self.start_requested_slave_instance.setAggregate('')
+    self.logout()
+    self.login(self.compute_node_user_id)
     self.commit()
 
     # 7th access
@@ -253,7 +256,7 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
 
 class TestSlapOSSlapToolComputeNodeAccess(TestSlapOSSlapToolMixin):
   def test_getFullComputerInformation(self):
-    self._makeComplexComputeNode(with_slave=True)
+    self._makeComplexComputeNode(self.project, with_slave=True)
 
     partition_1_root_instance_title = self.compute_node.partition1.getAggregateRelatedValue(
       portal_type='Software Instance').getSpecialiseValue().getTitle()
@@ -741,7 +744,7 @@ class TestSlapOSSlapToolComputeNodeAccess(TestSlapOSSlapToolMixin):
       'recmethod': 'reportComputeNodeBang'}])
 
   def test_computerBang(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.compute_node_bang_simulator = tempfile.mkstemp()[1]
     try:
       self.login(self.compute_node_user_id)
@@ -793,7 +796,7 @@ class TestSlapOSSlapToolComputeNodeAccess(TestSlapOSSlapToolMixin):
         os.unlink(self.compute_node_load_configuration_simulator)
   
   def test_not_accessed_getSoftwareInstallationStatus(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.compute_node_bang_simulator = tempfile.mkstemp()[1]
     self.login(self.compute_node_user_id)
     created_at = rfc1123_date(DateTime())
@@ -851,7 +854,7 @@ class TestSlapOSSlapToolComputeNodeAccess(TestSlapOSSlapToolMixin):
         "http://example.org/foo", self.compute_node_id)
 
   def test_destroyedSoftwareRelease_noDestroyRequested(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.login(self.compute_node_user_id)
     self.assertRaises(NotFound,
         self.portal_slap.destroyedSoftwareRelease,
@@ -859,7 +862,7 @@ class TestSlapOSSlapToolComputeNodeAccess(TestSlapOSSlapToolMixin):
         self.compute_node_id)
 
   def test_destroyedSoftwareRelease_destroyRequested(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.login(self.compute_node_user_id)
     destroy_requested = self.destroy_requested_software_installation
     self.assertEqual(destroy_requested.getValidationState(), "validated")
@@ -868,7 +871,7 @@ class TestSlapOSSlapToolComputeNodeAccess(TestSlapOSSlapToolMixin):
     self.assertEqual(destroy_requested.getValidationState(), "invalidated")
 
   def test_availableSoftwareRelease(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.compute_node_bang_simulator = tempfile.mkstemp()[1]
     self.login(self.compute_node_user_id)
     software_installation = self.start_requested_software_installation
@@ -917,7 +920,7 @@ class TestSlapOSSlapToolComputeNodeAccess(TestSlapOSSlapToolMixin):
     self.assertXMLEqual(expected_xml, got_xml)
 
   def test_buildingSoftwareRelease(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.compute_node_bang_simulator = tempfile.mkstemp()[1]
     self.login(self.compute_node_user_id)
     software_installation = self.start_requested_software_installation
@@ -966,7 +969,7 @@ class TestSlapOSSlapToolComputeNodeAccess(TestSlapOSSlapToolMixin):
     self.assertXMLEqual(expected_xml, got_xml)
 
   def test_softwareReleaseError(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.compute_node_bang_simulator = tempfile.mkstemp()[1]
     self.login(self.compute_node_user_id)
     software_installation = self.start_requested_software_installation
@@ -1119,7 +1122,7 @@ class TestSlapOSSlapToolComputeNodeAccess(TestSlapOSSlapToolMixin):
 
 class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
   def test_getComputerPartitionCertificate(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     self.login(self.start_requested_software_instance.getUserId())
@@ -1153,7 +1156,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
     self.assertXMLEqual(expected_xml, got_xml)
 
   def test_getFullComputerInformation(self):
-    self._makeComplexComputeNode(with_slave=True)
+    self._makeComplexComputeNode(self.project, with_slave=True)
     self.login(self.start_requested_software_instance.getUserId())
     response = self.portal_slap.getFullComputerInformation(self.compute_node_id)
     self.assertEqual(200, response.status)
@@ -1294,7 +1297,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
     self.assertXMLEqual(expected_xml, got_xml)
 
   def test_getComputerPartitionStatus(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     created_at = rfc1123_date(DateTime())
@@ -1343,7 +1346,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
     self.assertXMLEqual(expected_xml, got_xml)
 
   def test_getComputerPartitionStatus_visited(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     created_at = rfc1123_date(DateTime())
@@ -1395,7 +1398,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
     self.assertXMLEqual(expected_xml, got_xml)
 
   def test_registerComputerPartition_withSlave(self):
-    self._makeComplexComputeNode(with_slave=True)
+    self._makeComplexComputeNode(self.project, with_slave=True)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     self.login(self.start_requested_software_instance.getUserId())
@@ -1525,7 +1528,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
     self.assertXMLEqual(expected_xml, got_xml)
 
   def test_registerComputerPartition(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     self.login(self.start_requested_software_instance.getUserId())
@@ -1644,7 +1647,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
       'recmethod': 'updateConnection'}])
 
   def test_setConnectionXml_withSlave(self):
-    self._makeComplexComputeNode(with_slave=True)
+    self._makeComplexComputeNode(self.project, with_slave=True)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     slave_reference = self.start_requested_slave_instance.getReference()
@@ -1678,7 +1681,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
         os.unlink(self.instance_update_connection_simulator)
 
   def test_setConnectionXml(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     connection_xml = """<marshal>
@@ -1711,7 +1714,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
         os.unlink(self.instance_update_connection_simulator)
 
   def test_softwareInstanceError(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     self.login(self.start_requested_software_instance.getUserId())
@@ -1758,7 +1761,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
     self.assertXMLEqual(expected_xml, got_xml)
 
   def test_softwareInstanceError_twice(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     self.login(self.start_requested_software_instance.getUserId())
@@ -1862,7 +1865,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
       'recmethod': 'bang'}])
 
   def test_softwareInstanceBang(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.instance_bang_simulator = tempfile.mkstemp()[1]
     try:
       partition_id = self.start_requested_software_instance.getAggregateValue(
@@ -1924,7 +1927,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
       'recmethod': 'rename'}])
 
   def test_softwareInstanceRename(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.instance_rename_simulator = tempfile.mkstemp()[1]
     try:
       partition_id = self.start_requested_software_instance.getAggregateValue(
@@ -1944,7 +1947,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
         os.unlink(self.instance_rename_simulator)
       
   def test_destroyedComputePartition(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     partition_id = self.destroy_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     ssl_key = self.destroy_requested_software_instance.getSslKey()
@@ -1972,7 +1975,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
       'recmethod': 'requestInstance'}])
 
   def test_request_withSlave(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.instance_request_simulator = tempfile.mkstemp()[1]
     try:
       partition_id = self.start_requested_software_instance.getAggregateValue(
@@ -1990,6 +1993,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
           filter_xml='<marshal><dictionary id="i2"/></marshal>',
           state='<marshal><string>started</string></marshal>',
           shared_xml='<marshal><bool>1</bool></marshal>',
+          project_reference=self.project.getReference()
           )
       self.assertEqual(408, response.status)
       self.assertEqual('private',
@@ -2001,13 +2005,15 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
           'state': 'started',
           'sla_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
           'software_type': 'req_type',
-          'shared': True})
+          'shared': True,
+          'project_reference': self.project.getReference()
+      })
     finally:
       if os.path.exists(self.instance_request_simulator):
         os.unlink(self.instance_request_simulator)
 
   def test_request(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.instance_request_simulator = tempfile.mkstemp()[1]
     try:
       partition_id = self.start_requested_software_instance.getAggregateValue(
@@ -2025,6 +2031,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
           filter_xml='<marshal><dictionary id="i2"/></marshal>',
           state='<marshal><string>started</string></marshal>',
           shared_xml='<marshal><bool>0</bool></marshal>',
+          project_reference=self.project.getReference()
           )
       self.assertEqual(408, response.status)
       self.assertEqual('private',
@@ -2036,13 +2043,15 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
           'state': 'started',
           'sla_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
           'software_type': 'req_type',
-          'shared': False})
+          'shared': False,
+          'project_reference': self.project.getReference()
+      })
     finally:
       if os.path.exists(self.instance_request_simulator):
         os.unlink(self.instance_request_simulator)
 
   def test_request_stopped(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.instance_request_simulator = tempfile.mkstemp()[1]
     try:
       partition_id = self.stop_requested_software_instance.getAggregateValue(
@@ -2060,6 +2069,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
           filter_xml='<marshal><dictionary id="i2"/></marshal>',
           state='<marshal><string>started</string></marshal>',
           shared_xml='<marshal><bool>0</bool></marshal>',
+          project_reference=self.project.getReference()
           )
       self.assertEqual(408, response.status)
       self.assertEqual('private',
@@ -2071,13 +2081,15 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
           'state': 'stopped',
           'sla_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
           'software_type': 'req_type',
-          'shared': False})
+          'shared': False,
+          'project_reference': self.project.getReference()
+      })
     finally:
       if os.path.exists(self.instance_request_simulator):
         os.unlink(self.instance_request_simulator)
 
   def test_updateInstanceSuccessorList(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
 
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
@@ -2133,7 +2145,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
             self.start_requested_software_instance.getSuccessorTitleList())
 
   def test_updateInstanceSuccessorList_one_child(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
 
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
@@ -2168,7 +2180,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
               self.start_requested_software_instance.getSuccessorTitleList())
 
   def test_updateInstanceSuccessorList_no_child(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
 
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
@@ -2205,7 +2217,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
               self.start_requested_software_instance.getSuccessorTitleList())
 
   def test_stoppedComputePartition(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     self.login(self.start_requested_software_instance.getUserId())
@@ -2251,7 +2263,7 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
     self.assertXMLEqual(expected_xml, got_xml)
 
   def test_startedComputePartition(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     self.login(self.start_requested_software_instance.getUserId())
@@ -2298,33 +2310,23 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
 
   def test_getSoftwareReleaseListFromSoftwareProduct(self):
     new_id = self.generateNewId()
-    software_product = self._makeSoftwareProduct(new_id)
+    software_product = self._makeSoftwareProduct(self.project, new_id=new_id,
+                                                 url='http://example.org/1.cfg')
     # 2 published software releases
-    software_release1 = self._makeSoftwareRelease(new_id)
-    software_release2 = self._makeSoftwareRelease(self.generateNewId())
-    software_release1.publish()
-    software_release2.publish()
-    # 1 released software release, should not appear
-    software_release3 = self._makeSoftwareRelease(new_id)
-    self.assertTrue(software_release3.getValidationState() == 'released')
+    software_release1 = software_product.contentValues(portal_type='Software Product Release Variation')[0]
+    software_release2 = self._makeSoftwareRelease(software_product, url='http://example.org/2.cfg')
+
     software_release1.edit(
-        aggregate_value=software_product.getRelativeUrl(),
-        url_string='http://example.org/1.cfg',
         effective_date=DateTime()
     )
     software_release2.edit(
-        aggregate_value=software_product.getRelativeUrl(),
-        url_string='http://example.org/2.cfg',
         effective_date=DateTime()
     )
-    software_release3.edit(
-        aggregate_value=software_product.getRelativeUrl(),
-        url_string='http://example.org/3.cfg'
-    )
     self.tic()
 
     response = self.portal_slap.getSoftwareReleaseListFromSoftwareProduct(
-        software_product.getReference())
+      self.project.getReference(),
+      software_product_reference=software_product.getReference())
     got_xml = etree.tostring(etree.fromstring(response),
         pretty_print=True, encoding="UTF-8", xml_declaration=True)
     expected_xml = """\
@@ -2335,39 +2337,32 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
     <string>%s</string>
   </list>
 </marshal>
-""" % (software_release2.getUrlString(), software_release1.getUrlString())
+""" % (software_release1.getUrlString(), software_release2.getUrlString())
     self.assertXMLEqual(expected_xml, got_xml)
   
   def test_getSoftwareReleaseListFromSoftwareProduct_effectiveDate(self):
     new_id = self.generateNewId()
-    software_product = self._makeSoftwareProduct(new_id)
+    software_product = self._makeSoftwareProduct(self.project, new_id=new_id,
+                                                 url='http://example.org/1.cfg')
     # 3 published software releases
-    software_release1 = self._makeSoftwareRelease(new_id)
-    software_release2 = self._makeSoftwareRelease(self.generateNewId())
-    software_release3 = self._makeSoftwareRelease(self.generateNewId())
-    software_release1.publish()
-    software_release2.publish()
-    software_release3.publish()
+    software_release1 = software_product.contentValues(portal_type="Software Product Release Variation")[0]
+    software_release2 = self._makeSoftwareRelease(software_product, url='http://example.org/2.cfg')
+    software_release3 = self._makeSoftwareRelease(software_product, url='http://example.org/3.cfg')
     software_release1.edit(
-        aggregate_value=software_product.getRelativeUrl(),
-        url_string='http://example.org/1.cfg',
         effective_date=(DateTime() - 1)
     )
     # Should not be considered yet!
     software_release2.edit(
-        aggregate_value=software_product.getRelativeUrl(),
-        url_string='http://example.org/2.cfg',
         effective_date=(DateTime() + 1)
     )
     software_release3.edit(
-        aggregate_value=software_product.getRelativeUrl(),
-        url_string='http://example.org/3.cfg',
         effective_date=DateTime()
     )
     self.tic()
 
     response = self.portal_slap.getSoftwareReleaseListFromSoftwareProduct(
-        software_product.getReference())
+      self.project.getReference(),
+      software_product_reference=software_product.getReference())
     # check returned XML
     got_xml = etree.tostring(etree.fromstring(response),
         pretty_print=True, encoding="UTF-8", xml_declaration=True)
@@ -2386,10 +2381,11 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
 
   def test_getSoftwareReleaseListFromSoftwareProduct_emptySoftwareProduct(self):
     new_id = self.generateNewId()
-    software_product = self._makeSoftwareProduct(new_id)
+    software_product = self._makeSoftwareProduct(self.project, new_id=new_id)
 
     response = self.portal_slap.getSoftwareReleaseListFromSoftwareProduct(
-        software_product.getReference())
+      self.project.getReference(),
+      software_product_reference=software_product.getReference())
     got_xml = etree.tostring(etree.fromstring(response),
         pretty_print=True, encoding="UTF-8", xml_declaration=True)
     expected_xml = """\
@@ -2402,7 +2398,8 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
 
   def test_getSoftwareReleaseListFromSoftwareProduct_NoSoftwareProduct(self):
     response = self.portal_slap.getSoftwareReleaseListFromSoftwareProduct(
-        'Can I has a nonexistent software product?')
+      self.project.getReference(),
+      software_product_reference='Can I has a nonexistent software product?')
     got_xml = etree.tostring(etree.fromstring(response),
         pretty_print=True, encoding="UTF-8", xml_declaration=True)
     expected_xml = """\
@@ -2415,31 +2412,17 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
 
   def test_getSoftwareReleaseListFromSoftwareProduct_fromUrl(self):
     new_id = self.generateNewId()
-    software_product = self._makeSoftwareProduct(new_id)
+    software_product = self._makeSoftwareProduct(self.project, new_id=new_id,
+                                                 url='http://example.org/1.cfg')
     # 2 published software releases
-    software_release1 = self._makeSoftwareRelease(new_id)
-    software_release2 = self._makeSoftwareRelease(self.generateNewId())
-    software_release1.publish()
-    software_release2.publish()
-    # 1 released software release, should not appear
-    software_release3 = self._makeSoftwareRelease(new_id)
-    self.assertTrue(software_release3.getValidationState() == 'released')
-    software_release1.edit(
-        aggregate_value=software_product.getRelativeUrl(),
-        url_string='http://example.org/1.cfg'
-    )
-    software_release2.edit(
-        aggregate_value=software_product.getRelativeUrl(),
-        url_string='http://example.org/2.cfg'
-    )
-    software_release3.edit(
-        aggregate_value=software_product.getRelativeUrl(),
-        url_string='http://example.org/3.cfg'
-    )
+    software_release1 = software_product.contentValues(portal_type='Software Product Release Variation')[0]
+    software_release2 = self._makeSoftwareRelease(software_product, url='http://example.org/2.cfg')
+
     self.tic()
 
     response = self.portal_slap.getSoftwareReleaseListFromSoftwareProduct(
-        software_release_url=software_release2.getUrlString())
+      self.project.getReference(),
+      software_release_url=software_release2.getUrlString())
     # check returned XML
     got_xml = etree.tostring(etree.fromstring(response),
         pretty_print=True, encoding="UTF-8", xml_declaration=True)
@@ -2451,18 +2434,26 @@ class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
     <string>%s</string>
   </list>
 </marshal>
-""" % (software_release2.getUrlString(), software_release1.getUrlString())
+""" % (software_release1.getUrlString(), software_release2.getUrlString())
     self.assertXMLEqual(expected_xml, got_xml)
 
 
 class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
   def afterSetUp(self):
-    password = "%s-1Aa$" % self.generateNewId() 
+    TestSlapOSSlapToolMixin.afterSetUp(self)
+
+    password = "%s-1Aa$" % self.generateNewId()
     reference = 'test_%s' % self.generateNewId()
-    person = self.portal.person_module.newContent(portal_type='Person',
+    person = self.portal.person_module.newContent(
+      portal_type='Person',
       title=reference,
-      reference=reference)
-    person.newContent(portal_type='Assignment', role='member').open()
+      reference=reference
+    )
+    person.newContent(
+      portal_type='Assignment',
+      function='customer',
+      destination_project_value=self.project
+    ).open()
     person.newContent(portal_type='ERP5 Login',
       reference=reference, password=password).validate()
 
@@ -2470,7 +2461,7 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
     self.person = person
     self.person_reference = person.getReference()
     self.person_user_id = person.getUserId()
-    TestSlapOSSlapToolMixin.afterSetUp(self)
+    self.tic()
 
   def test_not_accessed_getComputerStatus(self):
     self.login(self.person_user_id)
@@ -2596,7 +2587,7 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
         os.unlink(self.compute_node_bang_simulator)
 
   def test_getComputerPartitionStatus(self):
-    self._makeComplexComputeNode()
+    self._makeComplexComputeNode(self.project)
     self.login(self.person_user_id)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
@@ -2646,7 +2637,7 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
     self.assertXMLEqual(expected_xml, got_xml)
 
   def test_getComputerPartitionStatus_visited(self):
-    self._makeComplexComputeNode(person=self.person)
+    self._makeComplexComputeNode(self.project, person=self.person)
     self.login(self.person_user_id)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
@@ -2700,7 +2691,7 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
     self.assertXMLEqual(expected_xml, got_xml)
 
   def test_registerComputerPartition_withSlave(self):
-    self._makeComplexComputeNode(person=self.person, with_slave=True)
+    self._makeComplexComputeNode(self.project, person=self.person, with_slave=True)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     self.login(self.person_user_id)
@@ -2830,7 +2821,7 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
     self.assertXMLEqual(expected_xml, got_xml)
 
   def test_registerComputerPartition(self):
-    self._makeComplexComputeNode(person=self.person)
+    self._makeComplexComputeNode(self.project, person=self.person)
     partition_id = self.start_requested_software_instance.getAggregateValue(
         portal_type='Compute Partition').getReference()
     self.login(self.person_user_id)
@@ -2949,7 +2940,7 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
       'recmethod': 'bang'}])
 
   def test_softwareInstanceBang(self):
-    self._makeComplexComputeNode(person=self.person)
+    self._makeComplexComputeNode(self.project, person=self.person)
     self.instance_bang_simulator = tempfile.mkstemp()[1]
     try:
       partition_id = self.start_requested_software_instance.getAggregateValue(
@@ -3013,7 +3004,7 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
       'recmethod': 'rename'}])
 
   def test_softwareInstanceRename(self):
-    self._makeComplexComputeNode(person=self.person)
+    self._makeComplexComputeNode(self.project, person=self.person)
     self.instance_rename_simulator = tempfile.mkstemp()[1]
     try:
       partition_id = self.start_requested_software_instance.getAggregateValue(
@@ -3053,6 +3044,7 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
           filter_xml='<marshal><dictionary id="i2"/></marshal>',
           state='<marshal><string>started</string></marshal>',
           shared_xml='<marshal><bool>1</bool></marshal>',
+          project_reference=self.project.getReference()
           )
       self.assertEqual(408, response.status)
       self.assertEqual('private',
@@ -3064,7 +3056,9 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
           'state': 'started',
           'sla_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
           'software_type': 'req_type',
-          'shared': True})
+          'shared': True,
+          'project_reference': self.project.getReference()
+      })
     finally:
       if os.path.exists(self.instance_request_simulator):
         os.unlink(self.instance_request_simulator)
@@ -3083,6 +3077,39 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
           filter_xml='<marshal><dictionary id="i2"/></marshal>',
           state='<marshal><string>started</string></marshal>',
           shared_xml='<marshal><bool>0</bool></marshal>',
+          project_reference=self.project.getReference()
+          )
+      self.assertEqual(408, response.status)
+      self.assertEqual('private',
+          response.headers.get('cache-control'))
+      self.assertInstanceRequestSimulator((), {
+          'instance_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
+          'software_title': 'req_reference',
+          'software_release': 'req_release',
+          'state': 'started',
+          'sla_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
+          'software_type': 'req_type',
+          'shared': False,
+          'project_reference': self.project.getReference()
+      })
+    finally:
+      if os.path.exists(self.instance_request_simulator):
+        os.unlink(self.instance_request_simulator)
+
+  def test_requestWithoutProjectReference(self):
+    self.instance_request_simulator = tempfile.mkstemp()[1]
+    try:
+      self.login(self.person_user_id)
+      self.person.requestSoftwareInstance = Simulator(
+        self.instance_request_simulator, 'requestSoftwareInstance')
+      response = self.portal_slap.requestComputerPartition(
+          software_release='req_release',
+          software_type='req_type',
+          partition_reference='req_reference',
+          partition_parameter_xml='<marshal><dictionary id="i2"/></marshal>',
+          filter_xml='<marshal><dictionary id="i2"/></marshal>',
+          state='<marshal><string>started</string></marshal>',
+          shared_xml='<marshal><bool>0</bool></marshal>'
           )
       self.assertEqual(408, response.status)
       self.assertEqual('private',
@@ -3094,7 +3121,9 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
           'state': 'started',
           'sla_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
           'software_type': 'req_type',
-          'shared': False})
+          'shared': False,
+          'project_reference': self.project.getReference()
+      })
     finally:
       if os.path.exists(self.instance_request_simulator):
         os.unlink(self.instance_request_simulator)
@@ -3105,19 +3134,21 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
       default_email_coordinate_text="%s@example.org" % self.person.getReference(),
       career_role='member',
     )
-    self._makeComplexComputeNode(person=self.person)
+    self._makeComplexComputeNode(self.project, person=self.person)
     self.start_requested_software_instance.updateLocalRolesOnSecurityGroups()
     self.tic()
     self.login(self.person_user_id)
-    response = self.portal_slap.requestComputerPartition(
-      software_release=self.start_requested_software_instance.getUrlString(),
-      software_type=self.start_requested_software_instance.getSourceReference(),
-      partition_reference=self.start_requested_software_instance.getTitle(),
-      partition_parameter_xml='<marshal><dictionary id="i2"/></marshal>',
-      filter_xml='<marshal><dictionary id="i2"/></marshal>',
-      state='<marshal><string>started</string></marshal>',
-      shared_xml='<marshal><bool>0</bool></marshal>',
-      )
+    with TemporaryAlarmScript(self.portal, 'Item_getSubscriptionStatus', "'subscribed'"):
+      response = self.portal_slap.requestComputerPartition(
+        software_release=self.start_requested_software_instance.getUrlString(),
+        software_type=self.start_requested_software_instance.getSourceReference(),
+        partition_reference=self.start_requested_software_instance.getTitle(),
+        partition_parameter_xml='<marshal><dictionary id="i2"/></marshal>',
+        filter_xml='<marshal><dictionary id="i2"/></marshal>',
+        state='<marshal><string>started</string></marshal>',
+        shared_xml='<marshal><bool>0</bool></marshal>',
+        project_reference=self.project.getReference()
+        )
     self.assertEqual(type(response), str)
     # check returned XML
     got_xml = etree.tostring(etree.fromstring(response),
@@ -3229,7 +3260,7 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
       compute_node_id = 'Foo Compute Node'
       compute_node_reference = 'live_comp_%s' % self.generateNewId()
       self.portal.REQUEST.set('compute_node_reference', compute_node_reference)
-      response = self.portal_slap.requestComputer(compute_node_id)
+      response = self.portal_slap.requestComputer(compute_node_id, self.project.getReference())
       got_xml = etree.tostring(etree.fromstring(response),
         pretty_print=True, encoding="UTF-8", xml_declaration=True)
       expected_xml = """\
@@ -3248,7 +3279,8 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
 """ % {'compute_node_id': compute_node_reference}
 
       self.assertXMLEqual(expected_xml, got_xml)
-      self.assertRequestComputeNodeSimulator((), {'compute_node_title': compute_node_id})
+      self.assertRequestComputeNodeSimulator((), {'compute_node_title': compute_node_id,
+                                                  'project_reference': self.project.getReference()})
     finally:
       if os.path.exists(self.compute_node_request_compute_node_simulator):
         os.unlink(self.compute_node_request_compute_node_simulator)
diff --git a/master/bt5/slapos_slap_tool/TestTemplateItem/portal_components/test.erp5.testSlapOSSlapToolComputeNodeUpdateFromDict.py b/master/bt5/slapos_slap_tool/TestTemplateItem/portal_components/test.erp5.testSlapOSSlapToolComputeNodeUpdateFromDict.py
index 171fa6a27..8b302e711 100644
--- a/master/bt5/slapos_slap_tool/TestTemplateItem/portal_components/test.erp5.testSlapOSSlapToolComputeNodeUpdateFromDict.py
+++ b/master/bt5/slapos_slap_tool/TestTemplateItem/portal_components/test.erp5.testSlapOSSlapToolComputeNodeUpdateFromDict.py
@@ -5,8 +5,8 @@ class TestSlapOSCoreComputeNodeUpdateFromDict(SlapOSTestCaseMixinWithAbort):
 
   def afterSetUp(self):
     SlapOSTestCaseMixinWithAbort.afterSetUp(self)
-    self.compute_node = self.portal.compute_node_module.template_compute_node\
-        .Base_createCloneDocument(batch_mode=1)
+    self.compute_node = self.portal.compute_node_module\
+        .newContent(portal_type="Compute Node")
     self.compute_node.edit(
       reference='TESTC-%s' % self.generateNewId(),
     )
diff --git a/master/bt5/slapos_slap_tool/ToolComponentTemplateItem/portal_components/tool.erp5.SlapTool.py b/master/bt5/slapos_slap_tool/ToolComponentTemplateItem/portal_components/tool.erp5.SlapTool.py
index c6665fd28..6ea35cdab 100644
--- a/master/bt5/slapos_slap_tool/ToolComponentTemplateItem/portal_components/tool.erp5.SlapTool.py
+++ b/master/bt5/slapos_slap_tool/ToolComponentTemplateItem/portal_components/tool.erp5.SlapTool.py
@@ -373,7 +373,7 @@ class SlapTool(BaseTool):
 
   security.declareProtected(Permissions.AccessContentsInformation,
     'getSoftwareReleaseListFromSoftwareProduct')
-  def getSoftwareReleaseListFromSoftwareProduct(self,
+  def getSoftwareReleaseListFromSoftwareProduct(self, project_reference,
       software_product_reference=None, software_release_url=None):
     """
     Get the list of all published Software Releases related to one of either:
@@ -384,12 +384,23 @@ class SlapTool(BaseTool):
     If referenced Software Product does not exist, return empty list.
     If referenced Software Release does not exist, raise.
     """
+    project_list = self.getPortalObject().portal_catalog.portal_catalog(
+      portal_type='Project',
+      reference=project_reference,
+      validation_state='validated',
+      limit=2
+    )
+    if len(project_list) != 1:
+      raise NotImplementedError("%i projects '%s'" % (len(project_list), project_reference))
+    project = project_list[0]
+
     if software_product_reference is None:
       assert(software_release_url is not None)
       software_product_reference = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
-        portal_type='Software Release',
+        portal_type='Software Product Release Variation',
+        parent__follow_up__uid=project.getUid(),
         url_string=software_release_url
-      )[0].getObject().getAggregateValue().getReference()
+      )[0].getObject().getParentValue().getReference()
     else:
       # Don't accept both parameters
       assert(software_release_url is None)
@@ -397,13 +408,14 @@ class SlapTool(BaseTool):
     software_product_list = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
       portal_type='Software Product',
       reference=software_product_reference,
+      follow_up__uid=project.getUid(),
       validation_state='published')
     if len(software_product_list) is 0:
       return dumps([])
     if len(software_product_list) > 1:
       raise NotImplementedError('Several Software Product with the same title.')
     software_release_list = \
-        software_product_list[0].getObject().getAggregateRelatedValueList()
+        software_product_list[0].getObject().contentValues(portal_type='Software Product Release Variation')
     
     def sortkey(software_release):
       publication_date = software_release.getEffectiveDate()
@@ -420,9 +432,7 @@ class SlapTool(BaseTool):
     )
     return dumps(
       [software_release.getUrlString()
-        for software_release in software_release_list
-          if software_release.getValidationState() in \
-                  ['published', 'published_alive']])
+        for software_release in software_release_list])
 
   security.declareProtected(Permissions.AccessContentsInformation,
     'getHateoasUrl')
@@ -508,20 +518,20 @@ class SlapTool(BaseTool):
     return self._supplySupply(url, computer_id, state)
 
   @convertToREST
-  def _requestComputeNode(self, compute_node_title):
+  def _requestComputeNode(self, compute_node_title, project_reference):
     portal = self.getPortalObject()
     person = portal.portal_membership.getAuthenticatedMember().getUserValue()
-    person.requestComputeNode(compute_node_title=compute_node_title)
+    person.requestComputeNode(compute_node_title=compute_node_title, project_reference=project_reference)
     compute_node = ComputeNode(self.REQUEST.get('compute_node_reference').decode("UTF-8"))
     return dumps(compute_node)
 
   security.declareProtected(Permissions.AccessContentsInformation,
     'requestComputer')
-  def requestComputer(self, computer_title):
+  def requestComputer(self, computer_title, project_reference):
     """
     Request Compute Node
     """
-    return self._requestComputeNode(computer_title)
+    return self._requestComputeNode(computer_title, project_reference)
 
   security.declareProtected(Permissions.AccessContentsInformation,
     'buildingSoftwareRelease')
@@ -615,7 +625,7 @@ class SlapTool(BaseTool):
   def requestComputerPartition(self, computer_id=None,
       computer_partition_id=None, software_release=None, software_type=None,
       partition_reference=None, partition_parameter_xml=None,
-      filter_xml=None, state=None, shared_xml=_MARKER):
+      filter_xml=None, state=None, shared_xml=_MARKER, project_reference=None):
     """
     Asynchronously requests creation of compute partition for assigned
     parameters
@@ -629,7 +639,8 @@ class SlapTool(BaseTool):
     """
     return self._requestComputePartition(computer_id, computer_partition_id,
         software_release, software_type, partition_reference,
-        shared_xml, partition_parameter_xml, filter_xml, state)
+        shared_xml, partition_parameter_xml, filter_xml, state,
+        project_reference)
 
   security.declareProtected(Permissions.AccessContentsInformation,
     'useComputer')
@@ -926,7 +937,8 @@ class SlapTool(BaseTool):
   @convertToREST
   def _requestComputePartition(self, compute_node_id, compute_partition_id,
         software_release, software_type, partition_reference,
-        shared_xml, partition_parameter_xml, filter_xml, state):
+        shared_xml, partition_parameter_xml, filter_xml, state,
+        project_reference):
     """
     Asynchronously requests creation of compute partition for assigned
     parameters
@@ -964,7 +976,8 @@ class SlapTool(BaseTool):
               instance_xml=castToStr(partition_parameter_kw),
               shared=shared,
               sla_xml=castToStr(filter_kw),
-              state=state)
+              state=state,
+              project_reference=project_reference)
 
     portal = self.getPortalObject()
     if compute_node_id and compute_partition_id:
@@ -978,8 +991,48 @@ class SlapTool(BaseTool):
     else:
       # requested as root, so done by human
       requester = portal.portal_membership.getAuthenticatedMember().getUserValue()
+
+      if project_reference is None:
+        # Compatibility with the slapos console client
+        # which does not send any project_reference parameter
+        # and always connect to a uniq url
+        # Try to guess the project reference automatically
+        project_list = portal.portal_catalog(portal_type='Project', limit=2)
+        if len(project_list) == 1:
+          # If the user only has access to one project
+          # we can easily suppose the request must be allocated here
+          kw['project_reference'] = project_list[0].getReference()
+        else:
+          release_variation_list = portal.portal_catalog(
+            portal_type='Software Product Release Variation',
+            url_string=software_release,
+            limit=2
+          )
+          if len(release_variation_list) == 1:
+            # If the user only has access to matching release variation
+            # we can easily suppose the request must be allocated on the same project
+            kw['project_reference'] = release_variation_list[0].getParentValue().getFollowUpReference()
+
+          # Finally, try to use the SLA parameter to guess where it could be
+          elif 'project_guid' in filter_kw:
+            kw['project_reference'] = filter_kw['project_guid']
+          elif 'computer_guid' in filter_kw:
+            computer_list = portal.portal_catalog(
+              portal_type=['Compute Node', 'Remote Node', 'Instance Node'],
+              limit=2
+            )
+            if len(computer_list == 1):
+              kw['project_reference'] = computer_list[0].getFollowUpReference()
+          elif 'network_guid' in filter_kw:
+            network_list = portal.portal_catalog(
+              portal_type='Computer Network',
+              limit=2
+            )
+            if len(network_list == 1):
+              kw['project_reference'] = network_list[0].getFollowUpReference()
+
       key = '_'.join([requester.getRelativeUrl(), partition_reference])
-    
+
     last_data = requester.getLastData(key)
     requested_software_instance = None
     value = dict(
@@ -989,7 +1042,7 @@ class SlapTool(BaseTool):
     if last_data is not None and isinstance(last_data, type(value)):
       requested_software_instance = self.restrictedTraverse(
           last_data.get('request_instance'), None)
-    
+
     if last_data is None or not isinstance(last_data, type(value)) or \
       last_data.get('hash') != value['hash'] or \
       requested_software_instance is None:
-- 
2.30.9