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

slapos_pdm: alarm to destroy software installation

parent 68b252a2
......@@ -8,7 +8,7 @@
<dictionary>
<item>
<key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_destroySoftwareInstallationWithArchivedSoftwareRelease</string> </value>
<value> <string>Alarm_destroyUnusedSoftwareInstallation</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
......@@ -26,7 +26,7 @@
</item>
<item>
<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>
<key> <string>periodicity_hour</string> </key>
......@@ -95,7 +95,7 @@
</item>
<item>
<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>
</dictionary>
</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()
catalog_kw = dict(
portal.portal_catalog.searchAndActivate(
method_id = 'SoftwareInstallation_destroyIfUnused',
method_kw = {'tag': tag},
activate_kw = {'tag':tag},
portal_type='Software Installation',
validation_state='validated',
url_string=context.getUrlString(),
**{"slapos_item.slap_state": "start_requested"}
)
portal.portal_catalog.searchAndActivate(
method_id = 'SoftwareInstallation_destroyWithSoftwareReleaseArchived',
activate_kw = {'tag':tag},
**catalog_kw
)
context.activate(after_tag=tag).getId()
......@@ -54,7 +54,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Alarm_destroySoftwareInstallationWithArchivedSoftwareRelease</string> </value>
<value> <string>Alarm_destroyUnusedSoftwareInstallation</string> </value>
</item>
</dictionary>
</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 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SoftwareInstallation_destroyWithSoftwareReleaseArchived</string> </value>
<value> <string>SoftwareInstallation_destroyIfUnused</string> </value>
</item>
</dictionary>
</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):
self.assertNotEqual('Visited by InstanceTree_createUpgradeDecision',
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 @@
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSPDMDestroySoftwareInstallationWithArchivedSoftwareReleaseAlarm</string> </value>
<value> <string>testSlapOSPDMDestroySoftwareInstallation</string> </value>
</item>
<item>
<key> <string>description</string> </key>
......@@ -18,7 +18,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSPDMDestroySoftwareInstallationWithArchivedSoftwareReleaseAlarm</string> </value>
<value> <string>test.erp5.testSlapOSPDMDestroySoftwareInstallation</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......
......@@ -86,6 +86,8 @@ class TestSlapOSPDMMixinSkins(SlapOSTestCaseMixin):
instance = instance_tree.getSuccessorValue()
instance.edit(aggregate_value=partition)
partition.markFree()
partition.markBusy()
self.tic()
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_upgrade_decision_process_started
upgrade_decision_module/template_upgrade_decision
\ No newline at end of file
test.erp5.testSlapOSPDMAlarm
test.erp5.testSlapOSPDMSkins
test.erp5.testSlapOSPDMDestroySoftwareInstallationWithArchivedSoftwareReleaseAlarm
test.erp5.testSlapOSPDMDestroySoftwareInstallation
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