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