diff --git a/master/bt5/slapos_cloud/PathTemplateItem/portal_alarms/slapos_cloud_create_missing_subscription_assignment.xml b/master/bt5/slapos_cloud/PathTemplateItem/portal_alarms/slapos_cloud_create_missing_subscription_assignment.xml new file mode 100644 index 0000000000000000000000000000000000000000..7acc3fa20e6a5adb9de9b2cabe59447c5ee03911 --- /dev/null +++ b/master/bt5/slapos_cloud/PathTemplateItem/portal_alarms/slapos_cloud_create_missing_subscription_assignment.xml @@ -0,0 +1,72 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="Alarm" module="erp5.portal_type"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>active_sense_method_id</string> </key> + <value> <string>Alarm_createMissingSubscriptionAssignment</string> </value> + </item> + <item> + <key> <string>automatic_solve</string> </key> + <value> <int>0</int> </value> + </item> + <item> + <key> <string>description</string> </key> + <value> + <none/> + </value> + </item> + <item> + <key> <string>enabled</string> </key> + <value> <int>1</int> </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>slapos_cloud_create_missing_subscription_assignment</string> </value> + </item> + <item> + <key> <string>periodicity_day_frequency</string> </key> + <value> <int>1</int> </value> + </item> + <item> + <key> <string>periodicity_hour</string> </key> + <value> + <tuple/> + </value> + </item> + <item> + <key> <string>periodicity_minute</string> </key> + <value> + <tuple/> + </value> + </item> + <item> + <key> <string>periodicity_month</string> </key> + <value> + <tuple/> + </value> + </item> + <item> + <key> <string>periodicity_month_day</string> </key> + <value> + <tuple/> + </value> + </item> + <item> + <key> <string>periodicity_week</string> </key> + <value> + <tuple/> + </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string>Create Missing Subscription Assignment</string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Alarm_createMissingSubscriptionAssignment.py b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Alarm_createMissingSubscriptionAssignment.py new file mode 100644 index 0000000000000000000000000000000000000000..5c0d70363a86b7adcdf347e1635a079690bc6aae --- /dev/null +++ b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Alarm_createMissingSubscriptionAssignment.py @@ -0,0 +1,27 @@ +portal = context.getPortalObject() + +assignment_category_list = portal.portal_preferences.getPreferredSubscriptionAssignmentCategoryList() +is_default_project_defined = False +for assignment_category in assignment_category_list: + if assignment_category.startswith('destination_project/project_module/'): + is_default_project_defined = True + +if not is_default_project_defined: + return + +portal.portal_catalog.searchAndActivate( + portal_type='Assignment', + # Do not create assignment if the Person does not have at least one + validation_state='open', + # Prevent checking the same person multiple times + group_by=['parent_uid'], + method_id='Assignment_createPersonMissingSubscriptionAssignment', + # This alarm bruteforce checking all documents, + # without changing them directly. + # Increase priority to not block other activities + method_args=[assignment_category_list], + method_kw={"activate_kw": {'tag': tag, 'priority': 2}}, + activate_kw={'tag': tag, 'priority': 2} +) + +context.activate(after_tag=tag).getId() diff --git a/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Alarm_createMissingSubscriptionAssignment.xml b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Alarm_createMissingSubscriptionAssignment.xml new file mode 100644 index 0000000000000000000000000000000000000000..cb739ef3e4383adc61275b1a03d0727bdcb5c4cd --- /dev/null +++ b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Alarm_createMissingSubscriptionAssignment.xml @@ -0,0 +1,62 @@ +<?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, fixit, params</string> </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>Alarm_createMissingSubscriptionAssignment</string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Assignment_createPersonMissingSubscriptionAssignment.py b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Assignment_createPersonMissingSubscriptionAssignment.py new file mode 100644 index 0000000000000000000000000000000000000000..549163e409903aa2e1dc755615c2ae33937b7188 --- /dev/null +++ b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Assignment_createPersonMissingSubscriptionAssignment.py @@ -0,0 +1,26 @@ +from zExceptions import Unauthorized +if REQUEST is not None: + raise Unauthorized + +assignment_category_set = set(assignment_category_list) +person = context.getParentValue() + +# check if user has an assignment matching the default preference +has_default_assignment = False +has_open_assignment = False +for assignment in person.contentValues( + portal_type='Assignment' +): + if assignment.getValidationState() != 'open': + continue + has_open_assignment = True + has_default_assignment = has_default_assignment or (assignment_category_set <= set(assignment.getCategoryList())) + +if has_open_assignment and (not has_default_assignment): + new_assignment = person.newContent( + portal_type="Assignment", + title="Preferred Subscription Assignment" + ) + new_assignment.setCategoryList(assignment_category_list) + new_assignment.open(comment="Created during the system preference subscription definition") + new_assignment.reindexObject(activate_kw=activate_kw) diff --git a/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Assignment_createPersonMissingSubscriptionAssignment.xml b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Assignment_createPersonMissingSubscriptionAssignment.xml new file mode 100644 index 0000000000000000000000000000000000000000..ea845d287d4b4b52dfb5e66f3e9ceb4322a791e0 --- /dev/null +++ b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/Assignment_createPersonMissingSubscriptionAssignment.xml @@ -0,0 +1,62 @@ +<?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>assignment_category_list, activate_kw=None, REQUEST=None</string> </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>Assignment_createPersonMissingSubscriptionAssignment</string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudAlarm.py b/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudAlarm.py index 6857f2077236ffa8b6997c9de61232592ffc7e9d..8803699291e613cf17d9d2337205b56042407089 100644 --- a/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudAlarm.py +++ b/master/bt5/slapos_cloud/TestTemplateItem/portal_components/test.erp5.testSlapOSCloudAlarm.py @@ -7,6 +7,134 @@ from erp5.component.module.DateUtils import addToDate class TestSlapOSCoreSlapOSAssertInstanceTreeSuccessorAlarm(SlapOSTestCaseMixin): + ################################################################# + # slapos_cloud_create_missing_subscription_assignment + ################################################################# + def test_Assignment_createPersonMissingSubscriptionAssignment_alarm_notOpenAssignment(self): + # some preparation + project = self.portal.project_module.newContent() + preference = self.portal.portal_preferences.slapos_default_system_preference + preference.edit( + preferred_subscription_assignment_category_list=[ + 'destination_project/%s' % project.getRelativeUrl() + ] + ) + + person = self.portal.person_module.newContent( + portal_type='Person' + ) + assignment = person.newContent( + portal_type='Assignment' + ) + self.tic() + self._test_alarm_not_visited( + self.portal.portal_alarms.slapos_cloud_create_missing_subscription_assignment, + assignment, + 'Assignment_createPersonMissingSubscriptionAssignment' + ) + + def test_Assignment_createPersonMissingSubscriptionAssignment_alarm_oneAssignment(self): + # some preparation + project = self.portal.project_module.newContent() + preference = self.portal.portal_preferences.slapos_default_system_preference + preference.edit( + preferred_subscription_assignment_category_list=[ + 'destination_project/%s' % project.getRelativeUrl() + ] + ) + + person = self.portal.person_module.newContent( + portal_type='Person' + ) + assignment = person.newContent( + portal_type='Assignment' + ) + assignment.open() + self.tic() + self._test_alarm( + self.portal.portal_alarms.slapos_cloud_create_missing_subscription_assignment, + assignment, + 'Assignment_createPersonMissingSubscriptionAssignment' + ) + + """ + def test_Assignment_createPersonMissingSubscriptionAssignment_alarm_twoAssignment(self): + person = self.portal.person_module.newContent( + portal_type='Person' + ) + assignment1 = person.newContent( + portal_type='Assignment' + ) + assignment1.open() + assignment2 = person.newContent( + portal_type='Assignment' + ) + assignment2.open() + self.tic() + self._test_alarm( + self.portal.portal_alarms.slapos_cloud_create_missing_subscription_assignment, + assignment1, + 'Assignment_createPersonMissingSubscriptionAssignment' + ) + """ + + ################################################################# + # Assignment_createPersonMissingSubscriptionAssignment + ################################################################# + def test_Assignment_createPersonMissingSubscriptionAssignment_script_noSubscriptionAssignment(self): + subscription_category_list = ['activity/research', 'role/member'] + person = self.portal.person_module.newContent( + portal_type='Person' + ) + assignment = person.newContent( + portal_type='Assignment' + ) + assignment.open() + + assignment.Assignment_createPersonMissingSubscriptionAssignment(subscription_category_list) + + self.assertEquals(2, len(person.contentValues(portal_type='Assignment'))) + subscription_assignment = [x for x in person.contentValues(portal_type='Assignment') if x.getId() != assignment.getId()][0] + self.assertEquals('open', subscription_assignment.getValidationState()) + self.assertSameSet(subscription_category_list, subscription_assignment.getCategoryList()) + + def test_Assignment_createPersonMissingSubscriptionAssignment_script_withOpenSubscriptionAssignment(self): + subscription_category_list = ['activity/research', 'role/member'] + person = self.portal.person_module.newContent( + portal_type='Person' + ) + assignment = person.newContent( + portal_type='Assignment' + ) + assignment.setCategoryList(subscription_category_list) + assignment.open() + + assignment.Assignment_createPersonMissingSubscriptionAssignment(subscription_category_list) + + self.assertEquals(1, len(person.contentValues(portal_type='Assignment'))) + + def test_Assignment_createPersonMissingSubscriptionAssignment_script_withDraftSubscriptionAssignment(self): + subscription_category_list = ['activity/research', 'role/member'] + person = self.portal.person_module.newContent( + portal_type='Person' + ) + assignment1 = person.newContent( + portal_type='Assignment' + ) + assignment1.setCategoryList(subscription_category_list) + + assignment2 = person.newContent( + portal_type='Assignment' + ) + assignment2.open() + + assignment2.Assignment_createPersonMissingSubscriptionAssignment(subscription_category_list) + + self.assertEquals(3, len(person.contentValues(portal_type='Assignment'))) + subscription_assignment = [x for x in person.contentValues(portal_type='Assignment') if (x.getId() != assignment1.getId()) and (x.getId() != assignment2.getId())][0] + self.assertEquals('open', subscription_assignment.getValidationState()) + self.assertSameSet(subscription_category_list, subscription_assignment.getCategoryList()) + ################################################################# # slapos_assert_instance_tree_successor ################################################################# diff --git a/master/bt5/slapos_cloud/bt/template_path_list b/master/bt5/slapos_cloud/bt/template_path_list index 3dc08867b0d95145ccff304cf49c60ca7d954011..ae6d25d25b8bf41c81909432ae9af5c743fce6c4 100644 --- a/master/bt5/slapos_cloud/bt/template_path_list +++ b/master/bt5/slapos_cloud/bt/template_path_list @@ -3,6 +3,7 @@ acl_users/slapos_machine acl_users/slapos_shadow portal_alarms/slapos_allocate_instance portal_alarms/slapos_assert_instance_tree_successor +portal_alarms/slapos_cloud_create_missing_subscription_assignment portal_alarms/slapos_cloud_garbage_collect_one_time_virtual_master_access_token portal_alarms/slapos_cloud_invalidate_closed_compute_node portal_alarms/slapos_cloud_invalidate_destroyed_instance