From e6d9f4fa82c1dff1ceccfb9bba792646fddb17d6 Mon Sep 17 00:00:00 2001
From: Romain Courteaud <romain@nexedi.com>
Date: Wed, 25 Jan 2023 13:33:44 +0000
Subject: [PATCH] slapos_cloud:

* drop not needed log
* commit module business_application
* fix SLA tests
---
 .../hosting_subscription_module.xml           |   3 +
 .../instance_tree_module.xml                  |   3 +
 .../software_installation_module.xml          |   3 +
 .../software_instance_module.xml              |   3 +
 .../slapos_cloud/Person_findPartition.py      |   2 -
 .../test.erp5.SlapOSTestCaseMixin.py          |  87 +++++++++-
 ...est.erp5.testSlapOSCloudAllocationAlarm.py | 154 +++++++-----------
 .../test.erp5.testSlapOSCloudUpgrader.py      |   6 +-
 8 files changed, 160 insertions(+), 101 deletions(-)

diff --git a/master/bt5/slapos_cloud/ModuleTemplateItem/hosting_subscription_module.xml b/master/bt5/slapos_cloud/ModuleTemplateItem/hosting_subscription_module.xml
index 8b5bcdde4..d22c7e201 100644
--- a/master/bt5/slapos_cloud/ModuleTemplateItem/hosting_subscription_module.xml
+++ b/master/bt5/slapos_cloud/ModuleTemplateItem/hosting_subscription_module.xml
@@ -1,4 +1,7 @@
 <module>
+ <category_list>
+  <category>business_application/slapos</category>
+ </category_list>
  <id>hosting_subscription_module</id>
  <permission_list>
   <permission type='tuple'>
diff --git a/master/bt5/slapos_cloud/ModuleTemplateItem/instance_tree_module.xml b/master/bt5/slapos_cloud/ModuleTemplateItem/instance_tree_module.xml
index 270efa7eb..f0673de3b 100644
--- a/master/bt5/slapos_cloud/ModuleTemplateItem/instance_tree_module.xml
+++ b/master/bt5/slapos_cloud/ModuleTemplateItem/instance_tree_module.xml
@@ -1,4 +1,7 @@
 <module>
+ <category_list>
+  <category>business_application/slapos</category>
+ </category_list>
  <id>instance_tree_module</id>
  <permission_list>
   <permission type='tuple'>
diff --git a/master/bt5/slapos_cloud/ModuleTemplateItem/software_installation_module.xml b/master/bt5/slapos_cloud/ModuleTemplateItem/software_installation_module.xml
index 8aba6b714..05caca6d7 100644
--- a/master/bt5/slapos_cloud/ModuleTemplateItem/software_installation_module.xml
+++ b/master/bt5/slapos_cloud/ModuleTemplateItem/software_installation_module.xml
@@ -1,4 +1,7 @@
 <module>
+ <category_list>
+  <category>business_application/slapos</category>
+ </category_list>
  <id>software_installation_module</id>
  <permission_list>
   <permission type='tuple'>
diff --git a/master/bt5/slapos_cloud/ModuleTemplateItem/software_instance_module.xml b/master/bt5/slapos_cloud/ModuleTemplateItem/software_instance_module.xml
index e11b31697..2b522e46a 100644
--- a/master/bt5/slapos_cloud/ModuleTemplateItem/software_instance_module.xml
+++ b/master/bt5/slapos_cloud/ModuleTemplateItem/software_instance_module.xml
@@ -1,4 +1,7 @@
 <module>
+ <category_list>
+  <category>business_application/slapos</category>
+ </category_list>
  <id>software_instance_module</id>
  <permission_list>
   <permission type='tuple'>
diff --git a/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Person_findPartition.py b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Person_findPartition.py
index 2d3f7642c..a6f6ae01c 100644
--- a/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Person_findPartition.py
+++ b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Person_findPartition.py
@@ -180,8 +180,6 @@ if test_mode:
 
 SQL_WINDOW_SIZE = 50
 
-context.log(portal.portal_catalog.countResults(src__=1, **query_kw))
-
 # fetch at mot 50 random Compute Partitions, and check if they are ok
 isTransitionPossible = portal.portal_workflow.isTransitionPossible
 result_count = portal.portal_catalog.countResults(**query_kw)[0][0]
diff --git a/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.SlapOSTestCaseMixin.py b/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.SlapOSTestCaseMixin.py
index 3e40b129f..bdd10d7ab 100644
--- a/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.SlapOSTestCaseMixin.py
+++ b/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.SlapOSTestCaseMixin.py
@@ -81,7 +81,7 @@ def withAbort(func):
 
 class TemporaryAlarmScript(object):
   """
-  Context manager for temporary python scripts
+  Context manager for temporary alarm python scripts
   """
   def __init__(self, portal, script_name, fake_return="", attribute=None):
     self.script_name = script_name
@@ -611,6 +611,91 @@ class SlapOSTestCaseMixin(testSlapOSMixin):
       url_string='type%s' % self.generateNewId(),
     )
 
+  def bootstrapAllocableInstanceTree(self, is_allocated=False, shared=False, node="compute"):
+    project = self.addProject()
+    person = self.makePerson(project)
+    software_product = self._makeSoftwareProduct(project)
+    release_variation = software_product.contentValues(portal_type='Software Product Release Variation')[0]
+    type_variation = software_product.contentValues(portal_type='Software Product Type Variation')[0]
+
+    self.tic()
+    if node == "compute":
+      person.requestComputeNode(compute_node_title='test compute node',
+                                project_reference=project.getReference())
+      self.tic()
+      compute_node = self.portal.portal_catalog.getResultValue(
+        portal_type='Compute Node',
+        reference=self.portal.REQUEST.get('compute_node_reference')
+      )
+      assert compute_node is not None
+      # The edit above will update capacity scope due the interaction workflow
+      # The line above force capacity scope to be open, keeping the previous
+      # behaviour.
+      compute_node.edit(capacity_scope='open')
+    elif node == "remote":
+      compute_node = self.portal.compute_node_module.newContent(
+        portal_type="Remote Node",
+        follow_up_value=project
+      )
+    elif node == "instance":
+      compute_node = self.portal.compute_node_module.newContent(
+        portal_type="Instance Node",
+        follow_up_value=project
+      )
+    else:
+      raise ValueError("Unsupported node value: %s" % node)
+
+    request_kw = dict(
+      software_release=release_variation.getUrlString(),
+      software_type=type_variation.getTitle(),
+      instance_xml=self.generateSafeXml(),
+      sla_xml=self.generateSafeXml(),
+      shared=shared,
+      software_title='test tree',
+      state='started',
+      project_reference=project.getReference()
+    )
+    person.requestSoftwareInstance(**request_kw)
+    instance_tree = self.portal.REQUEST.get('request_instance_tree')
+
+    if is_allocated:
+      if (node == "instance") and (shared):
+        real_compute_node = self.portal.compute_node_module.newContent(
+          portal_type="Compute Node",
+          follow_up_value=project
+        )
+        # The edit above will update capacity scope due the interaction workflow
+        # The line above force capacity scope to be open, keeping the previous
+        # behaviour.
+        real_compute_node.edit(capacity_scope='open')
+        real_compute_node.validate()
+        partition = real_compute_node.newContent(
+          portal_type='Compute Partition',
+          reference='reference%s' % self.generateNewId()
+        )
+        software_instance = self.portal.software_instance_module.newContent(
+          portal_type="Software Instance",
+          follow_up_value=project,
+          aggregate_value=partition
+        )
+        compute_node.edit(specialise_value=software_instance)
+      elif (node == "instance") and (not shared):
+        raise NotImplementedError('can not allocate on instance node')
+      else:
+        partition = compute_node.newContent(
+          portal_type='Compute Partition',
+          reference='reference%s' % self.generateNewId()
+        )
+
+      instance = instance_tree.getSuccessorValue()
+      instance.edit(aggregate_value=partition)
+      partition.validate()
+      partition.markFree()
+      partition.markBusy()
+
+    self.tic()
+    return software_product, release_variation, type_variation, compute_node, instance_tree
+
   def addAllocationSupply(self, title, node, software_product,
                           software_release, software_type,
                           destination_value=None,
diff --git a/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudAllocationAlarm.py b/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudAllocationAlarm.py
index 5a859f2ea..002f113b6 100644
--- a/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudAllocationAlarm.py
+++ b/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudAllocationAlarm.py
@@ -1,7 +1,6 @@
 # Copyright (c) 2002-2012 Nexedi SA and Contributors. All Rights Reserved.
 import transaction
-from erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixin, simulate
-from Products.ERP5Type.tests.utils import createZODBPythonScript
+from erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixin, simulate, TemporaryAlarmScript
 from unittest import skip
 
 
@@ -15,43 +14,11 @@ class TestSlapOSAllocation(SlapOSTestCaseMixin):
     SlapOSTestCaseMixin._makeTree(self, project,
                                   requested_template_id=requested_template_id)
 
-  def _simulatePerson_isAllowedToAllocate(self):
-    script_name = 'Person_isAllowedToAllocate'
-    if script_name in self.portal.portal_skins.custom.objectIds():
-      raise ValueError('Precondition failed: %s exists in custom' % script_name)
-    createZODBPythonScript(self.portal.portal_skins.custom,
-                        script_name,
-                        '*args, **kwargs',
-                        '# Script body\n'
-"""portal_workflow = context.portal_workflow
-portal_workflow.doActionFor(context, action='edit_action', comment='Visited by Person_isAllowedToAllocate')
-return True""" )
-    transaction.commit()
-
-  def _simulatePerson_isNotAllowedToAllocate(self):
-    script_name = 'Person_isAllowedToAllocate'
-    if script_name in self.portal.portal_skins.custom.objectIds():
-      raise ValueError('Precondition failed: %s exists in custom' % script_name)
-    createZODBPythonScript(self.portal.portal_skins.custom,
-                        script_name,
-                        '*args, **kwargs',
-                        '# Script body\n'
-"""return False""")
-    transaction.commit()
-
-  def _dropPerson_isAllowedToAllocate(self):
-    script_name = 'Person_isAllowedToAllocate'
-    if script_name in self.portal.portal_skins.custom.objectIds():
-      self.portal.portal_skins.custom.manage_delObjects(script_name)
-    transaction.commit()
-
   def test_person_allocation_checked(self):
     self._makeTree(self.project)
-    self._simulatePerson_isAllowedToAllocate()
-    try:
+    with TemporaryAlarmScript(self.portal, 'Person_isAllowedToAllocate',
+                              fake_return="True"):
       self.software_instance.SoftwareInstance_tryToAllocatePartition()
-    finally:
-      self._dropPerson_isAllowedToAllocate()
     self.assertEqual(
         'Visited by Person_isAllowedToAllocate',
         self.person_user.workflow_history['edit_workflow'][-1]['comment'])
@@ -65,11 +32,9 @@ return True""" )
 
     self.assertEqual(None, self.software_instance.getAggregateValue(
         portal_type='Compute Partition'))
-    self._simulatePerson_isNotAllowedToAllocate()
-    try:
+    with TemporaryAlarmScript(self.portal, 'Person_isAllowedToAllocate',
+                              fake_return="False"):
       self.software_instance.SoftwareInstance_tryToAllocatePartition()
-    finally:
-      self._dropPerson_isAllowedToAllocate()
     self.assertEqual(None, self.software_instance.getAggregateValue(
         portal_type='Compute Partition'))
     self.assertEqual(
@@ -229,34 +194,17 @@ return True""" )
         portal_type='Compute Partition'))
     transaction.abort()
 
-  def _simulateSoftwareInstance_tryToAllocatePartition(self):
-    script_name = 'SoftwareInstance_tryToAllocatePartition'
-    if script_name in self.portal.portal_skins.custom.objectIds():
-      raise ValueError('Precondition failed: %s exists in custom' % script_name)
-    createZODBPythonScript(self.portal.portal_skins.custom,
-                        script_name,
-                        '*args, **kwargs',
-                        '# Script body\n'
-"""portal_workflow = context.portal_workflow
-portal_workflow.doActionFor(context, action='edit_action', comment='Visited by SoftwareInstance_tryToAllocatePartition') """ )
-    transaction.commit()
-
-  def _dropSoftwareInstance_tryToAllocatePartition(self):
-    script_name = 'SoftwareInstance_tryToAllocatePartition'
-    if script_name in self.portal.portal_skins.custom.objectIds():
-      self.portal.portal_skins.custom.manage_delObjects(script_name)
-    transaction.commit()
-
+  ##################################################
+  # alarm slapos_allocate_instance
+  ##################################################
   @simulate('Person_isAllowedToAllocate', '*args, **kwargs', 'return True')
   def test_alarm_software_instance_unallocated(self):
     self._makeTree(self.project)
 
-    self._simulateSoftwareInstance_tryToAllocatePartition()
-    try:
+    with TemporaryAlarmScript(self.portal, 'SoftwareInstance_tryToAllocatePartition'):
       self.portal.portal_alarms.slapos_allocate_instance.activeSense()
       self.tic()
-    finally:
-      self._dropSoftwareInstance_tryToAllocatePartition()
+
     self.assertEqual(
         'Visited by SoftwareInstance_tryToAllocatePartition',
         self.software_instance.workflow_history['edit_workflow'][-1]['comment'])
@@ -265,12 +213,10 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by S
   def test_alarm_slave_instance_unallocated(self):
     self._makeSlaveTree(self.project)
 
-    self._simulateSoftwareInstance_tryToAllocatePartition()
-    try:
+    with TemporaryAlarmScript(self.portal, 'SoftwareInstance_tryToAllocatePartition'):
       self.portal.portal_alarms.slapos_allocate_instance.activeSense()
       self.tic()
-    finally:
-      self._dropSoftwareInstance_tryToAllocatePartition()
+
     self.assertEqual(
         'Visited by SoftwareInstance_tryToAllocatePartition',
         self.software_instance.workflow_history['edit_workflow'][-1]['comment'])
@@ -282,12 +228,11 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by S
     self._makeComputeNode(self.project)
     self.software_instance.setAggregate(self.partition.getRelativeUrl())
     self.tic()
-    self._simulateSoftwareInstance_tryToAllocatePartition()
-    try:
+
+    with TemporaryAlarmScript(self.portal, 'SoftwareInstance_tryToAllocatePartition'):
       self.portal.portal_alarms.slapos_allocate_instance.activeSense()
       self.tic()
-    finally:
-      self._dropSoftwareInstance_tryToAllocatePartition()
+
     self.assertNotEqual(
         'Visited by SoftwareInstance_tryToAllocatePartition',
         self.software_instance.workflow_history['edit_workflow'][-1]['comment'])
@@ -299,16 +244,18 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by S
     self._makeComputeNode(self.project)
     self.software_instance.setAggregate(self.partition.getRelativeUrl())
     self.tic()
-    self._simulateSoftwareInstance_tryToAllocatePartition()
-    try:
+
+    with TemporaryAlarmScript(self.portal, 'SoftwareInstance_tryToAllocatePartition'):
       self.portal.portal_alarms.slapos_allocate_instance.activeSense()
       self.tic()
-    finally:
-      self._dropSoftwareInstance_tryToAllocatePartition()
+
     self.assertNotEqual(
         'Visited by SoftwareInstance_tryToAllocatePartition',
         self.software_instance.workflow_history['edit_workflow'][-1]['comment'])
 
+  ##################################################
+  # SoftwareInstance_tryToAllocatePartition
+  ##################################################
   @simulate('Person_isAllowedToAllocate', '*args, **kwargs', 'return True')
   def test_allocation_computer_guid(self):
     self._makeTree(self.project)
@@ -729,50 +676,65 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by S
 
   def check_allocation_category_sla(self, base_category, compute_node_category,
                                     other_category):
-    self._makeTree(self.project)
+    software_product, release_variation, type_variation, compute_node, instance_tree = self.bootstrapAllocableInstanceTree()
+    self.addAllocationSupply("for compute node", compute_node, software_product,
+                             release_variation, type_variation)
+    self._installSoftware(
+      compute_node,
+      release_variation.getUrlString()
+    )
+    self.tic()
 
-    self._makeComputeNode(self.project)
-    self.assertEqual(self.compute_node.getAllocationScope(), "open")
-    self.assertEqual(self.compute_node.getCapacityScope(), "open")
-    self.compute_node.edit(**{base_category: compute_node_category})
-    self.assertEqual(self.compute_node.getAllocationScope(), "open")
-    self.assertEqual(self.compute_node.getCapacityScope(), "open")
+    partition = compute_node.newContent(
+      portal_type='Compute Partition',
+      reference='reference%s' % self.generateNewId()
+    )
+    partition.validate()
+    partition.markFree()
 
-    self._installSoftware(self.compute_node,
-        self.software_instance.getUrlString())
+    self.assertEqual(compute_node.getAllocationScope(), "open")
+    self.assertEqual(compute_node.getCapacityScope(), "open")
+    compute_node.edit(**{base_category: compute_node_category})
+    self.assertEqual(compute_node.getAllocationScope(), "open")
+    self.assertEqual(compute_node.getCapacityScope(), "open")
 
-    self.assertEqual(None, self.software_instance.getAggregateValue(
+    self.tic()
+
+    software_instance = instance_tree.getSuccessorValue()
+
+    self.assertEqual(None, software_instance.getAggregateValue(
         portal_type='Compute Partition'))
 
     # Another category
-    self.software_instance.setSlaXml("""<?xml version='1.0' encoding='utf-8'?>
+    software_instance.setSlaXml("""<?xml version='1.0' encoding='utf-8'?>
         <instance>
         <parameter id='%s'>%s</parameter>
         </instance>""" % (base_category, other_category))
-    self.software_instance.SoftwareInstance_tryToAllocatePartition()
+    software_instance.SoftwareInstance_tryToAllocatePartition()
     self.assertEqual(None,
-        self.software_instance.getAggregate(portal_type='Compute Partition'))
+        software_instance.getAggregate(portal_type='Compute Partition'))
 
     # No existing category
-    self.software_instance.setSlaXml("""<?xml version='1.0' encoding='utf-8'?>
+    software_instance.setSlaXml("""<?xml version='1.0' encoding='utf-8'?>
         <instance>
         <parameter id='%s'>foo</parameter>
         </instance>""" % (base_category))
-    self.software_instance.SoftwareInstance_tryToAllocatePartition()
+    software_instance.SoftwareInstance_tryToAllocatePartition()
     self.assertEqual(None,
-        self.software_instance.getAggregate(portal_type='Compute Partition'))
+        software_instance.getAggregate(portal_type='Compute Partition'))
 
     # Compute Node category
-    self.software_instance.setSlaXml("""<?xml version='1.0' encoding='utf-8'?>
+    software_instance.setSlaXml("""<?xml version='1.0' encoding='utf-8'?>
         <instance>
         <parameter id='%s'>%s</parameter>
         </instance>""" % (base_category, compute_node_category))
-    self.software_instance.SoftwareInstance_tryToAllocatePartition()
+    software_instance.SoftwareInstance_tryToAllocatePartition()
 
-    if self.software_instance.getAggregate(portal_type='Compute Partition') is None:
-      raise ValueError(self.software_instance)
-    self.assertEqual(self.partition.getRelativeUrl(),
-        self.software_instance.getAggregate(portal_type='Compute Partition'))
+    self.assertEqual(
+      compute_node.contentValues(portal_type='Compute Partition')[0].getRelativeUrl(),
+      software_instance.getAggregate(portal_type='Compute Partition'),
+      software_instance.getRelativeUrl()
+    )
 
   @simulate('Person_isAllowedToAllocate', '*args, **kwargs', 'return True')
   def test_allocation_group_sla(self):
diff --git a/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudUpgrader.py b/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudUpgrader.py
index 1f85dfb20..eb08b1eaf 100644
--- a/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudUpgrader.py
+++ b/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudUpgrader.py
@@ -157,13 +157,15 @@ class TestSlapOSCloudUpgrader(SlapOSTestCaseMixin):
     computer_module = self.portal.getDefaultModule('Computer')
 
     computer_nothing_to_migrate = computer_module.newContent(
-      portal_type='Computer'
+      portal_type='Computer',
+      title='not to migrate'
     )
 
     computer_to_migrate = computer_module.newContent(
       portal_type='Computer',
       quantity=99,
-      bar='foo3'
+      bar='foo3',
+      title='to migrate'
     )
 
     # Create fake workflow history
-- 
2.30.9