Commit 700c4cb0 authored by Rafael Monnerat's avatar Rafael Monnerat

slapos_crm: Add alarm to Automatic update support requests

Those alarms aims to improve the Administrator Decision, closing tickets which has no solution (destroyed instances) or suspending tickets which are resolved (or false positives) without Administrator intervention.
parent 7f1cab27
<?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_updateSupportRequestMonitoringState</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Check if a public or a friend computer contacted master recently and create a ticket if the computer stops to contact master after some time.</string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_crm_update_support_request_state</string> </value>
</item>
<item>
<key> <string>periodicity_hour</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_hour_frequency</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>periodicity_minute</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_minute_frequency</string> </key>
<value> <int>30</int> </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_start_date</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1288051200.0</float>
<string>GMT</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>periodicity_week</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Alarm</string> </value>
</item>
<item>
<key> <string>sense_method_id</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Update Support Request State</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Action Information" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_button</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>float_index</string> </key>
<value> <float>30.0</float> </value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>6</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>reference</string> </key>
<value> <string>rss_view</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>RSS Monitoring Support Request List</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/SupportRequestModule_viewMonitoringSupportRequestList?portal_skin=RSS</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:rss_feed_image</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
portal = context.getPortalObject()
default_resource_uid = portal.restrictedTraverse("service_module/slapos_crm_monitoring", None).getUid()
portal.portal_catalog.searchAndActivate(
portal_type='Support Request',
simulation_state='validated',
default_resource_uid=default_resource_uid,
method_id='SupportRequest_updateMonitoringState',
activate_kw = {'tag':tag}
)
context.activate(after_tag=tag).getId()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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_updateSupportRequestMonitoringState</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# Example code:
# Import a standard function, and get the HTML request and response objects.
from Products.PythonScripts.standard import html_quote
request = container.REQUEST
response = request.response
# Return a string identifying this script.
print "This is the", script.meta_type, '"%s"' % script.getId(),
if script.title:
print "(%s)" % html_quote(script.title),
print "in", container.absolute_url()
return printed
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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>id</string> </key>
<value> <string>SupportRequest_recheckMonitoringStatus</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
""" Close Support Request which are related to a Destroy Requested Instance. """
if context.getSimulationState() == "invalidated":
return
document = context.getSourceProjectValue()
if document is not None and document.getSlapState() == "destroy_requested":
person = context.getDestinationDecision(portal_type="Person")
if not person:
return
if context.getSimulationState() != "invalidated":
context.invalidate()
# Send Notification message
message = """ Closing this ticket as the Hosting Subscription was destroyed by the user.
"""
notification_reference = "slapos-crm-support-request-close-destroyed-notification"
portal = context.getPortalObject()
notification_message = portal.portal_notifications.getDocumentValue(
reference=notification_reference)
if notification_message is not None:
mapping_dict = {'hosting_subscription_title':document.getTitle()}
message = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':mapping_dict})
return context.SupportRequest_trySendNotificationMessage(
"Hosting Subscription was destroyed was destroyed by the user", message, person)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SupportRequest_updateMonitoringDestroyRequestedState</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from DateTime import DateTime
import json
from Products.ERP5Type.DateUtils import addToDate
portal = context.getPortalObject()
document = context.getSourceProjectValue()
if document is None:
return True
has_error = False
# Check if at least one software Instance is Allocated
for instance in document.getSpecialiseRelatedValueList(
portal_type=["Software Instance", "Slave Instance"]):
if instance.getSlapState() not in ["start_requested", "stop_requested"]:
continue
if instance.getAggregateValue() is not None:
if instance.getPortalType() == "Software Instance" and \
instance.SoftwareInstance_hasReportedError():
has_error = True
break
else:
has_error = True
break
if not has_error:
person = context.getDestinationDecision(portal_type="Person")
if not person:
return
if context.getSimulationState() == "validated":
context.suspend()
else:
return
# Send Notification message
message = """ Suspending this ticket as the problem is not present anymore.
"""
notification_reference = "slapos-crm-support-request-suspend-hs-notification"
notification_message = portal.portal_notifications.getDocumentValue(
reference=notification_reference)
if notification_message is not None:
mapping_dict = {'hosting_subscription_title':document.getTitle()}
message = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':mapping_dict})
return context.SupportRequest_trySendNotificationMessage(
"Suspending this ticket as the problem is not present anymore", message, person)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SupportRequest_updateMonitoringHostingSubscriptionState</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
if context.getSimulationState() == "invalidated":
return
document = context.getSourceProjectValue()
if document is None:
return True
if document.getPortalType() == "Computer":
return context.SupportRequest_updateMontoringComputerState()
if document.getPortalType() == "Hosting Subscription":
return context.SupportRequest_updateMonitoringHostingSubscriptionState()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SupportRequest_updateMonitoringState</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from DateTime import DateTime
import json
from Products.ERP5Type.DateUtils import addToDate
portal = context.getPortalObject()
if context.getSimulationState() == "invalidated":
return
document = context.getSourceProjectValue()
if document is not None and document.getPortalType() == "Computer":
memcached_dict = context.getPortalObject().portal_memcached.getMemcachedDict(
key_prefix='slap_tool',
plugin_path='portal_memcached/default_memcached_plugin')
try:
d = json.loads(memcached_dict[computer.getReference()])
last_contact = DateTime(d.get('created_at'))
if not ((DateTime() - last_contact) < 0.01):
return
except KeyError:
return
person = context.getDestinationDecision(portal_type="Person")
if not person:
return
if context.getSimulationState() != "suspended":
context.suspend()
# Send Notification message
message = """ Suspending this ticket as the computer contacted is contactin again.
"""
notification_reference = "slapos-crm-support-request-suspend-computer-back-notification"
notification_message = portal.portal_notifications.getDocumentValue(
reference=notification_reference)
if notification_message is not None:
mapping_dict = {'hosting_subscription_title':document.getTitle(),
'last_contact' : last_contact }
message = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':mapping_dict})
return context.SupportRequest_trySendNotificationMessage(
"Hosting Subscription was destroyed was destroyed by the user", message, person)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SupportRequest_updateMontoringComputerState</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -1110,5 +1110,64 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by H ...@@ -1110,5 +1110,64 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by H
self.assertNotEqual('Visited by HostingSubscription_checkSoftwareInstanceState', self.assertNotEqual('Visited by HostingSubscription_checkSoftwareInstanceState',
host_sub.workflow_history['edit_workflow'][-1]['comment']) host_sub.workflow_history['edit_workflow'][-1]['comment'])
class TestSlaposCrmUpdateSupportRequestState(testSlapOSMixin):
def beforeTearDown(self):
transaction.abort()
def _makeSupportRequest(self):
person = self.portal.person_module.template_member\
.Base_createCloneDocument(batch_mode=1)
support_request = self.portal.support_request_module.\
slapos_crm_support_request_template_for_monitoring\
.Base_createCloneDocument(batch_mode=1)
support_request.validate()
new_id = self.generateNewId()
support_request.edit(
title= "Support Request éçà %s" % new_id,
reference="TESTSRQ-%s" % new_id,
destination_decision_value=person
)
return support_request
def _simulateSupportRequest_updateMonitoringState(self):
script_name = 'SupportRequest_updateMonitoringState'
if script_name in self.portal.portal_skins.custom.objectIds():
raise ValueError('Precondition failed: %s exists in custom' % script_name)
createZODBPythonScript(self.portal.portal_skins.custom,
script_name,
'*args, **kw',
'# Script body\n'
"""portal_workflow = context.portal_workflow
portal_workflow.doActionFor(context, action='edit_action', comment='Visited by SupportRequest_updateMonitoringState') """ )
transaction.commit()
def _dropSupportRequest_updateMonitoringState(self):
script_name = 'SupportRequest_updateMonitoringState'
if script_name in self.portal.portal_skins.custom.objectIds():
self.portal.portal_skins.custom.manage_delObjects(script_name)
transaction.commit()
def test_alarm_update_support_request_state(self):
support_request = self._makeSupportRequest()
self._simulateSupportRequest_updateMonitoringState()
try:
self.portal.portal_alarms.slapos_crm_update_support_request_state.activeSense()
self.tic()
finally:
self._dropSupportRequest_updateMonitoringState()
self.assertEqual('Visited by SupportRequest_updateMonitoringState',
support_request.workflow_history['edit_workflow'][-1]['comment'])
...@@ -45,7 +45,9 @@ ...@@ -45,7 +45,9 @@
<item> <item>
<key> <string>text_content_warning_message</string> </key> <key> <string>text_content_warning_message</string> </key>
<value> <value>
<tuple/> <tuple>
<string>W:1127, 0: Cannot decode using encoding "ascii", unexpected byte at position 32 (invalid-encoded-data)</string>
</tuple>
</value> </value>
</item> </item>
<item> <item>
......
...@@ -8,4 +8,5 @@ service_module/slapos_crm_stop_acknowledgement ...@@ -8,4 +8,5 @@ service_module/slapos_crm_stop_acknowledgement
service_module/slapos_crm_delete_acknowledgement service_module/slapos_crm_delete_acknowledgement
service_module/slapos_crm_delete_reminder service_module/slapos_crm_delete_reminder
service_module/slapos_crm_monitoring service_module/slapos_crm_monitoring
service_module/slapos_crm_upgrade service_module/slapos_crm_upgrade
\ No newline at end of file portal_types/Support Request Module/6
\ No newline at end of file
event_module/slapos_crm_web_message_template event_module/slapos_crm_web_message_template
person_module/allocation_tester person_module/allocation_tester
portal_alarms/slapos_crm_cancel_invoice portal_alarms/slapos_crm_*
portal_alarms/slapos_crm_check_computer_state
portal_alarms/slapos_crm_check_instance_in_error
portal_alarms/slapos_crm_check_update_allocation_scope
portal_alarms/slapos_crm_check_update_personal_allocation_scope
portal_alarms/slapos_crm_create_regularisation_request
portal_alarms/slapos_crm_delete_hosting_subscription
portal_alarms/slapos_crm_invalidate_suspended_regularisation_request
portal_alarms/slapos_crm_stop_hosting_subscription
portal_alarms/slapos_crm_trigger_acknowledgment_escalation
portal_alarms/slapos_crm_trigger_delete_reminder_escalation
portal_alarms/slapos_crm_trigger_stop_acknowledgment_escalation
portal_alarms/slapos_crm_trigger_stop_reminder_escalation
portal_types/Support Request Module/6
regularisation_request_module/slapos_crm_regularisation_request_template regularisation_request_module/slapos_crm_regularisation_request_template
service_module/slapos_crm_acknowledgement service_module/slapos_crm_*
service_module/slapos_crm_complaint
service_module/slapos_crm_delete_acknowledgement
service_module/slapos_crm_delete_reminder
service_module/slapos_crm_information
service_module/slapos_crm_invoice_cancellation
service_module/slapos_crm_monitoring
service_module/slapos_crm_spam
service_module/slapos_crm_stop_acknowledgement
service_module/slapos_crm_stop_reminder
service_module/slapos_crm_upgrade
support_request_module/slapos_crm_support_request_template support_request_module/slapos_crm_support_request_template
support_request_module/slapos_crm_support_request_template_for_monitoring support_request_module/slapos_crm_support_request_template_for_monitoring
\ 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