Commit 17829b40 authored by Romain Courteaud's avatar Romain Courteaud 🐙

slapos_pdm: alarm to destroy software installation

parent 68b252a2
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>active_sense_method_id</string> </key> <key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_destroySoftwareInstallationWithArchivedSoftwareRelease</string> </value> <value> <string>Alarm_destroyUnusedSoftwareInstallation</string> </value>
</item> </item>
<item> <item>
<key> <string>automatic_solve</string> </key> <key> <string>automatic_solve</string> </key>
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>slapos_pdm_destroy_software_installation_with_archived_software_release</string> </value> <value> <string>slapos_pdm_destroy_unused_software_installation</string> </value>
</item> </item>
<item> <item>
<key> <string>periodicity_hour</string> </key> <key> <string>periodicity_hour</string> </key>
...@@ -95,7 +95,7 @@ ...@@ -95,7 +95,7 @@
</item> </item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
<value> <string>Destroy Software Installation related to archived Software Releases</string> </value> <value> <string>Destroy Unused Software Installation</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type='Software Release',
validation_state = 'archived',
method_id = 'SoftwareRelease_findAndDestroySoftwareInstallation',
method_kw = {'tag': tag},
activate_kw = {'tag':tag}
)
context.activate(after_tag=tag).getId()
if context.getValidationState() != 'archived':
return
portal = context.getPortalObject() portal = context.getPortalObject()
catalog_kw = dict( portal.portal_catalog.searchAndActivate(
method_id = 'SoftwareInstallation_destroyIfUnused',
method_kw = {'tag': tag},
activate_kw = {'tag':tag},
portal_type='Software Installation', portal_type='Software Installation',
validation_state='validated', validation_state='validated',
url_string=context.getUrlString(),
**{"slapos_item.slap_state": "start_requested"} **{"slapos_item.slap_state": "start_requested"}
) )
portal.portal_catalog.searchAndActivate( context.activate(after_tag=tag).getId()
method_id = 'SoftwareInstallation_destroyWithSoftwareReleaseArchived',
activate_kw = {'tag':tag},
**catalog_kw
)
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Alarm_destroySoftwareInstallationWithArchivedSoftwareRelease</string> </value> <value> <string>Alarm_destroyUnusedSoftwareInstallation</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
portal = context.getPortalObject()
software_installation = context
if software_installation.getValidationState() != 'validated':
return
if software_installation.getSlapState() != 'start_requested':
return
compute_node = software_installation.getAggregateValue(portal_type='Compute Node')
if compute_node is None:
return
# Search related software release
url_string = software_installation.getUrlString()
software_release = None
if url_string:
use_category_uid = portal.restrictedTraverse("portal_categories/use/trade/sale").getUid(),
product_list = portal.portal_catalog(
portal_type="Software Product",
validation_state=['validated', 'published'],
use__uid=use_category_uid,
follow_up__uid=compute_node.getFollowUpUid()
)
if len(product_list) != 0:
software_release = portal.portal_catalog.getResultValue(
portal_type="Software Product Release Variation",
url_string=url_string,
parent_uid=[x.getUid() for x in product_list]
)
if software_release is None:
return
software_product = software_release.getParentValue()
# If one Supply uses this software release, do nothing
for allocation_cell in portal.portal_catalog(
portal_type='Allocation Supply Cell',
# Follow up is not acquired.
# But as the software release object is project related, it should be ok to skip this filter
# follow_up__uid=software_product.getFollowUpUid(),
resource__uid=software_product.getUid(),
software_release__uid=software_release.getUid()
):
if allocation_cell.isAllocable():
return
# XXX check validation state
partition = portal.portal_catalog.getResultValue(
portal_type='Compute Partition',
parent_uid=compute_node.getUid(),
free_for_request=0,
software_release_url=url_string
)
if partition is not None:
return
software_installation.requestDestroy(
comment='Destroyed by %s as unused.' % script.id)
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>SoftwareInstallation_destroyWithSoftwareReleaseArchived</string> </value> <value> <string>SoftwareInstallation_destroyIfUnused</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
software_installation = context
url_string = software_installation.getUrlString()
if software_installation.getValidationState() != 'validated':
return
if software_installation.getSlapState() != 'start_requested':
return
software_release = software_installation.portal_catalog.getResultValue(
portal_type='Software Release',
validation_state='archived',
url_string=url_string
)
if software_release is None:
return
compute_node = software_installation.getAggregateValue(portal_type='Compute Node')
if compute_node is None:
return
if compute_node.ComputeNode_getSoftwareReleaseUsage(url_string) != 0:
return
if compute_node.getUpgradeScope() != 'auto':
# handle only Compute Nodes with automatic software management
return
software_installation.requestDestroy(
comment='Destroyed by %s as %s is archived.' % (script.id, software_release.getRelativeUrl(),))
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>tag</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SoftwareRelease_findAndDestroySoftwareInstallation</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -82,3 +82,21 @@ class TestSlapOSUpgradeDecisionProcess(SlapOSTestCaseMixin): ...@@ -82,3 +82,21 @@ class TestSlapOSUpgradeDecisionProcess(SlapOSTestCaseMixin):
self.assertNotEqual('Visited by InstanceTree_createUpgradeDecision', self.assertNotEqual('Visited by InstanceTree_createUpgradeDecision',
instance_tree.workflow_history['edit_workflow'][-1]['comment']) instance_tree.workflow_history['edit_workflow'][-1]['comment'])
def test_alarm_destroy_unused_software_installation(self):
software_installation = self.portal.software_installation_module.newContent(
portal_type='Software Installation'
)
self.portal.portal_workflow._jumpToStateFor(software_installation,
'start_requested')
self.portal.portal_workflow._jumpToStateFor(software_installation,
'validated')
self.tic()
with TemporaryAlarmScript(self.portal, 'SoftwareInstallation_destroyIfUnused'):
self.portal.portal_alarms.slapos_pdm_destroy_unused_software_installation.\
activeSense()
self.tic()
self.assertEqual('Visited by SoftwareInstallation_destroyIfUnused',
software_installation.workflow_history['edit_workflow'][-1]['comment'])
\ No newline at end of file
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
##############################################################################
from erp5.component.test.testSlapOSPDMSkins import TestSlapOSPDMMixinSkins
class TestSlapOSDestroySoftwareInstallation(TestSlapOSPDMMixinSkins):
def bootstrapSoftwareInstallation(self, is_allocated=True, is_supplied=True):
software_product, release_variation, type_variation, compute_node, _ = \
self.bootstrapAllocableInstanceTree(is_allocated=is_allocated)
software_installation = self.portal.software_installation_module.newContent(
portal_type='Software Installation',
follow_up_value=compute_node.getFollowUpValue(),
url_string=release_variation.getUrlString(),
aggregate_value=compute_node
)
self.portal.portal_workflow._jumpToStateFor(software_installation,
'start_requested')
self.portal.portal_workflow._jumpToStateFor(software_installation,
'validated')
supply = None
if is_supplied:
supply = self.addAllocationSupply("for compute node", compute_node, software_product,
release_variation, type_variation)
self.tic()
return software_installation, supply
########################################################
# Software Installation in expected state
########################################################
def test_destroyIfUnused_toDestroy(self):
software_installation, _ = self.bootstrapSoftwareInstallation(is_allocated=False, is_supplied=False)
software_installation.SoftwareInstallation_destroyIfUnused()
self.assertEqual('destroy_requested', software_installation.getSlapState())
self.assertEqual('validated', software_installation.getValidationState())
########################################################
# Software Installation not in expected state
########################################################
def test_destroyIfUnused_notStarted(self):
software_installation, _ = self.bootstrapSoftwareInstallation()
self.portal.portal_workflow._jumpToStateFor(software_installation,
'draft',
wf_id='installation_slap_interface_workflow')
software_installation.SoftwareInstallation_destroyIfUnused()
self.assertEqual('draft', software_installation.getSlapState())
self.assertEqual('validated', software_installation.getValidationState())
def test_destroyIfUnused_notValidated(self):
software_installation, _ = self.bootstrapSoftwareInstallation()
self.portal.portal_workflow._jumpToStateFor(software_installation,
'draft',
wf_id='item_workflow')
software_installation.SoftwareInstallation_destroyIfUnused()
self.assertEqual('start_requested', software_installation.getSlapState())
self.assertEqual('draft', software_installation.getValidationState())
########################################################
# Software Installation used
########################################################
def test_destroyIfUnused_oneInstanceUsed(self):
software_installation, _ = self.bootstrapSoftwareInstallation(is_allocated=True, is_supplied=False)
software_installation.SoftwareInstallation_destroyIfUnused()
self.assertEqual('start_requested', software_installation.getSlapState())
self.assertEqual('validated', software_installation.getValidationState())
def test_destroyIfUnused_oneSupply(self):
software_installation, _ = self.bootstrapSoftwareInstallation(is_allocated=False, is_supplied=True)
software_installation.SoftwareInstallation_destroyIfUnused()
self.assertEqual('start_requested', software_installation.getSlapState())
self.assertEqual('validated', software_installation.getValidationState())
########################################################
# Supply invalidated
########################################################
def test_destroyIfUnused_oneInvalidatedSupply(self):
software_installation, supply = self.bootstrapSoftwareInstallation(is_allocated=False, is_supplied=True)
supply.invalidate()
self.tic()
software_installation.SoftwareInstallation_destroyIfUnused()
self.assertEqual('start_requested', software_installation.getSlapState())
self.assertEqual('validated', software_installation.getValidationState())
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>default_reference</string> </key> <key> <string>default_reference</string> </key>
<value> <string>testSlapOSPDMDestroySoftwareInstallationWithArchivedSoftwareReleaseAlarm</string> </value> <value> <string>testSlapOSPDMDestroySoftwareInstallation</string> </value>
</item> </item>
<item> <item>
<key> <string>description</string> </key> <key> <string>description</string> </key>
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSPDMDestroySoftwareInstallationWithArchivedSoftwareReleaseAlarm</string> </value> <value> <string>test.erp5.testSlapOSPDMDestroySoftwareInstallation</string> </value>
</item> </item>
<item> <item>
<key> <string>portal_type</string> </key> <key> <string>portal_type</string> </key>
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
##############################################################################
from erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixin
from erp5.component.test.testSlapOSAccountingAlarm import simulateByEditWorkflowMark
class TestSlapOSDestroySoftwareInstallationWithArchivedSoftwareReleaseAlarm(SlapOSTestCaseMixin):
def createInstance(self, url_string):
instance_tree = self.portal.instance_tree_module\
.template_instance_tree.Base_createCloneDocument(batch_mode=1)
instance_tree.edit(
)
instance_tree.validate()
instance_tree.edit(
title=self.generateNewSoftwareTitle(),
reference="TESTHS-%s" % self.generateNewId(),
)
request_kw = dict(
software_release=url_string,
software_type=self.generateNewSoftwareType(),
instance_xml=self.generateSafeXml(),
sla_xml=self.generateSafeXml(),
shared=False,
software_title=instance_tree.getTitle(),
state='started'
)
instance_tree.requestStart(**request_kw)
instance_tree.requestInstance(**request_kw)
instance = instance_tree.getSuccessorValue()
self.tic()
return instance
def test_tocheck(self):
preference = self.portal.portal_preferences.getActiveSystemPreference()
preference.setPreferredCloudContractEnabled(True)
self.tic()
project = self.addProject()
compute_node, partition = self._makeComputeNode(project)
compute_node.setUpgradeScope('auto')
archived_url_string = self.generateNewSoftwareReleaseUrl()
# create software release
archived_software_release = self.portal.software_release_module.newContent(
portal_type='Software Release',
version='1',
url_string=archived_url_string
)
archived_software_release.publish()
archived_software_release.archive()
# install an software release
archived_software_installation = self.portal.software_installation_module\
.newContent(portal_type='Software Installation',
url_string=archived_url_string,
aggregate=compute_node.getRelativeUrl())
archived_software_installation.validate()
archived_software_installation.requestStart()
archived_used_url_string = self.generateNewSoftwareReleaseUrl()
# create software release
archived_used_software_release = self.portal.software_release_module.newContent(
portal_type='Software Release',
version='1',
url_string=archived_used_url_string
)
archived_used_software_release.publish()
archived_used_software_release.archive()
# install an software release
archived_used_software_installation = self.portal.software_installation_module\
.newContent(portal_type='Software Installation',
url_string=archived_used_url_string,
aggregate=compute_node.getRelativeUrl())
archived_used_software_installation.validate()
archived_used_software_installation.requestStart()
# use the software release
instance = self.createInstance(archived_used_url_string)
instance.setAggregate(partition.getRelativeUrl())
partition.markBusy()
self.tic()
published_url_string = self.generateNewSoftwareReleaseUrl()
# create software release
published_software_release = self.portal.software_release_module.newContent(
portal_type='Software Release',
version='1',
url_string=published_url_string
)
published_software_release.publish()
# install an software release
published_software_installation = self.portal.software_installation_module\
.newContent(portal_type='Software Installation',
url_string=published_url_string,
aggregate=compute_node.getRelativeUrl())
published_software_installation.validate()
published_software_installation.requestStart()
self.tic()
# first run touches software installation
self.stepCallSlaposPdmDestroySoftwareInstallationWithArchivedSoftwareReleaseAlarm()
self.tic()
self.assertEqual('destroy_requested', archived_software_installation.getSlapState())
self.assertEqual('validated', archived_software_installation.getValidationState())
self.assertEqual('start_requested', published_software_installation.getSlapState())
self.assertEqual('validated', published_software_installation.getValidationState())
self.assertEqual('start_requested', archived_used_software_installation.getSlapState())
self.assertEqual('validated', archived_used_software_installation.getValidationState())
# second run, but it is still not reported that software installation is destroyed
self.stepCallSlaposPdmDestroySoftwareInstallationWithArchivedSoftwareReleaseAlarm()
self.tic()
self.assertEqual('start_requested', published_software_installation.getSlapState())
self.assertEqual('validated', archived_used_software_installation.getValidationState())
self.assertEqual('start_requested', archived_used_software_installation.getSlapState())
def test_tocheck_manual_upgrade_scope(self):
preference = self.portal.portal_preferences.getActiveSystemPreference()
preference.setPreferredCloudContractEnabled(True)
self.tic()
project = self.addProject()
compute_node, partition = self._makeComputeNode(project)
compute_node.setUpgradeScope('confirmation')
archived_url_string = self.generateNewSoftwareReleaseUrl()
# create software release
archived_software_release = self.portal.software_release_module.newContent(
portal_type='Software Release',
version='1',
url_string=archived_url_string
)
archived_software_release.publish()
archived_software_release.archive()
# install an software release
archived_software_installation = self.portal.software_installation_module\
.newContent(portal_type='Software Installation',
url_string=archived_url_string,
aggregate=compute_node.getRelativeUrl())
archived_software_installation.validate()
archived_software_installation.requestStart()
archived_used_url_string = self.generateNewSoftwareReleaseUrl()
# create software release
archived_used_software_release = self.portal.software_release_module.newContent(
portal_type='Software Release',
version='1',
url_string=archived_used_url_string
)
archived_used_software_release.publish()
archived_used_software_release.archive()
# install an software release
archived_used_software_installation = self.portal.software_installation_module\
.newContent(portal_type='Software Installation',
url_string=archived_used_url_string,
aggregate=compute_node.getRelativeUrl())
archived_used_software_installation.validate()
archived_used_software_installation.requestStart()
# use the software release
instance = self.createInstance(archived_used_url_string)
instance.setAggregate(partition.getRelativeUrl())
partition.markBusy()
self.tic()
published_url_string = self.generateNewSoftwareReleaseUrl()
# create software release
published_software_release = self.portal.software_release_module.newContent(
portal_type='Software Release',
version='1',
url_string=published_url_string
)
published_software_release.publish()
# install an software release
published_software_installation = self.portal.software_installation_module\
.newContent(portal_type='Software Installation',
url_string=published_url_string,
aggregate=compute_node.getRelativeUrl())
published_software_installation.validate()
published_software_installation.requestStart()
self.tic()
# as Compute Node is manually managed, nothing happens
self.stepCallSlaposPdmDestroySoftwareInstallationWithArchivedSoftwareReleaseAlarm()
self.tic()
self.assertEqual('start_requested', archived_software_installation.getSlapState())
self.assertEqual('validated', archived_software_installation.getValidationState())
self.assertEqual('start_requested', published_software_installation.getSlapState())
self.assertEqual('validated', published_software_installation.getValidationState())
self.assertEqual('start_requested', archived_used_software_installation.getSlapState())
self.assertEqual('validated', archived_used_software_installation.getValidationState())
@simulateByEditWorkflowMark('SoftwareRelease_findAndDestroySoftwareInstallation')
def test_tocheck_no_op_run_software_release(self):
archived_software_release = self.portal.software_release_module.newContent(
portal_type='Software Release',
version='1',
url_string=self.generateNewSoftwareReleaseUrl(),
)
archived_software_release.publish()
archived_software_release.archive()
archived_cleaned_software_release = self.portal.software_release_module.newContent(
portal_type='Software Release',
version='1',
url_string=self.generateNewSoftwareReleaseUrl(),
)
archived_cleaned_software_release.publish()
archived_cleaned_software_release.archive()
published_software_release = self.portal.software_release_module.newContent(
portal_type='Software Release',
version='1',
url_string=self.generateNewSoftwareReleaseUrl(),
)
published_software_release.publish()
self.tic()
self.stepCallSlaposPdmDestroySoftwareInstallationWithArchivedSoftwareReleaseAlarm()
self.tic()
v = 'Visited by SoftwareRelease_findAndDestroySoftwareInstallation'
self.assertFalse(v in
[q['comment'] for q in published_software_release.workflow_history['edit_workflow']])
self.assertTrue(v in
[q['comment'] for q in archived_cleaned_software_release.workflow_history['edit_workflow']])
self.assertTrue(v in
[q['comment'] for q in archived_software_release.workflow_history['edit_workflow']])
@simulateByEditWorkflowMark('SoftwareInstallation_destroyWithSoftwareReleaseArchived')
def test_tocheck_no_op_run_software_installation(self):
preference = self.portal.portal_preferences.getActiveSystemPreference()
preference.setPreferredCloudContractEnabled(True)
self.tic()
project = self.addProject()
compute_node, partition = self._makeComputeNode(project)
partition.invalidate()
partition.markBusy()
url_string = self.generateNewSoftwareReleaseUrl()
archived_software_release = self.portal.software_release_module.newContent(
portal_type='Software Release',
version='1',
url_string=url_string,
)
archived_software_release.publish()
archived_software_release.archive()
software_installation_validated_request_start = self.portal.software_installation_module\
.newContent(portal_type='Software Installation',
url_string=url_string,
aggregate=compute_node.getRelativeUrl())
software_installation_validated_request_start.validate()
software_installation_validated_request_start.requestStart()
software_installation_validated_request_destroy = self.portal.software_installation_module\
.newContent(portal_type='Software Installation',
url_string=url_string,
aggregate=compute_node.getRelativeUrl())
software_installation_validated_request_destroy.validate()
software_installation_validated_request_destroy.requestStart()
software_installation_validated_request_destroy.requestDestroy()
software_installation_invalidated_request_destroy = self.portal.software_installation_module\
.newContent(portal_type='Software Installation',
url_string=url_string,
aggregate=compute_node.getRelativeUrl())
software_installation_invalidated_request_destroy.validate()
software_installation_invalidated_request_destroy.requestStart()
software_installation_invalidated_request_destroy.requestDestroy()
software_installation_invalidated_request_destroy.invalidate()
self.tic()
# sanity check
self.assertEqual('validated', software_installation_validated_request_start.getValidationState())
self.assertEqual('start_requested', software_installation_validated_request_start.getSlapState())
self.assertEqual('validated', software_installation_validated_request_destroy.getValidationState())
self.assertEqual('destroy_requested', software_installation_validated_request_destroy.getSlapState())
self.assertEqual('invalidated', software_installation_invalidated_request_destroy.getValidationState())
self.assertEqual('destroy_requested', software_installation_invalidated_request_destroy.getSlapState())
self.stepCallSlaposPdmDestroySoftwareInstallationWithArchivedSoftwareReleaseAlarm()
self.tic()
v = 'Visited by SoftwareInstallation_destroyWithSoftwareReleaseArchived'
self.assertTrue(v in
[q['comment'] for q in software_installation_validated_request_start.workflow_history['edit_workflow']])
self.assertFalse(v in
[q['comment'] for q in software_installation_validated_request_destroy.workflow_history['edit_workflow']])
self.assertFalse(v in
[q['comment'] for q in software_installation_invalidated_request_destroy.workflow_history['edit_workflow']])
\ No newline at end of file
...@@ -86,6 +86,8 @@ class TestSlapOSPDMMixinSkins(SlapOSTestCaseMixin): ...@@ -86,6 +86,8 @@ class TestSlapOSPDMMixinSkins(SlapOSTestCaseMixin):
instance = instance_tree.getSuccessorValue() instance = instance_tree.getSuccessorValue()
instance.edit(aggregate_value=partition) instance.edit(aggregate_value=partition)
partition.markFree()
partition.markBusy()
self.tic() self.tic()
return software_product, release_variation, type_variation, compute_node, instance_tree return software_product, release_variation, type_variation, compute_node, instance_tree
......
portal_alarms/slapos_pdm_destroy_software_installation_with_archived_software_release portal_alarms/slapos_pdm_destroy_unused_software_installation
portal_alarms/slapos_pdm_instance_tree_create_upgrade_decision portal_alarms/slapos_pdm_instance_tree_create_upgrade_decision
portal_alarms/slapos_pdm_upgrade_decision_process_started portal_alarms/slapos_pdm_upgrade_decision_process_started
upgrade_decision_module/template_upgrade_decision upgrade_decision_module/template_upgrade_decision
\ No newline at end of file
test.erp5.testSlapOSPDMAlarm test.erp5.testSlapOSPDMAlarm
test.erp5.testSlapOSPDMSkins test.erp5.testSlapOSPDMSkins
test.erp5.testSlapOSPDMDestroySoftwareInstallationWithArchivedSoftwareReleaseAlarm test.erp5.testSlapOSPDMDestroySoftwareInstallation
test.erp5.testSlapOSPDMCreateUpgradeDecisionSkins test.erp5.testSlapOSPDMCreateUpgradeDecisionSkins
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment