Most Powerful Open Source ERP

ERP5 How To Use Periodical Alarm

how to introducing alarms and how to use them.
  • Last Update:2017-04-27
  • Version:001
  • Language:en

What Is An Alarm

Alarms are in charge of periodical launch of a script and reporting its result. The script can be used to ensure the quantity of a resource or consistency or availability of a service and can span over multiple activities through an active process.

Alarms are capable of displaying the last result of the check process which was run in the background. The result can be provided either as a boolean value whether alarm was raised or not, or in the form of an HTML report which is intended to be displayed in a control center. Moreover, users may be notified automatically of alarm failures.

Alarms may also provide a solution if something wrong happens. This solution takes the form of a script which can be invoked by the administrator or by the user by clicking on a button displayed in the Alarm control center.

As Alarms are using activities extensively please take a look at how to use cmf actvities.

Create an Alarm

Alarm itself is created within ERP5 UI by following "My Favorites" -> "Configure Alarms". This will bring you to https://<erp5-url>/portal_alarms/view.

On the right side of the page, you will find  

  • Active Sense - is the most important field. It points to a script which is periodically launched as an Activity. Example value can be "Alarm_NoSales". The script itself can be longer running (couples of seconds) and should post results encapsulated in erp5.product.CMFActivity.ActiveResult by asynchronous call postResult (see example below).
  • Sense [optional] - interprets results of Active Sense. The field points to a script which is run synchronously on alarm view. By default it reports the last result of Active Sense. If you do specify your own script, it has to return boolean indicating whether there is an error or not (thus True=Error, False=OK).
  • Solve - script to repair what is broken. If no script is defined here the Active Sense script will be called with extra parameter fixit=1
  • Report - view to show a report which has been computed by Active Sense script

The Alarm base class is defined in https://lab.nexedi.com/nexedi/erp5/blob/master/product/ERP5/Document/Alarm.py

Example Alarm

# Example Alarm script "Alarm_NoSales" called periodically by Alarm -> Active Sense
# `context` in the script is the Alarm object itself
# Parameters: 
#   fixit=0 set by the alarm to 0 or 1. When 1 this script should try to repeat what's broken
#   tag=None can be passed further into activities

from Products.CMFActivity.ActiveResult import ActiveResult

OK = 0
FAIL = 100
active_result = ActiveResult("Expect failure by default", FAIL)
if complicated_evaluation:
  # for edit the keyword names are mandatory (constructor's args are `summary` and `severity` as well)
  active_result.edit(summary="It is alright", severity=OK)

# Publish alarm result - you can publish multiple results
context.newActiveProcess().postResult(active_result)

# return is useless in practice but might be good for testing
return active_result.isError() 

Example of Test of an Alarm

class TestMyModule(SecurityTestCase):
  def test_alarm(self):  
    # let's freeze the date during test because many alarms are working with time
    self.pinDateTime(creation_date)
    other_environment_preparations()

    alarm, = (a for a in self.getAlarmTool().getAlarmList() if a.getId() == 'check_lte_request_created')
    alarm.activeSense()
    self.tic() # we have to wait for the background alarm activities to finish
    process = alarm.getLastActiveProcess()
    results = process.getResultList()
    self.failIfEqual(process, None)  # process must had been launched
    self.assertEqual(len(results), 1)  # you can check number on results (you can post as many results as needed)
    self.assertTrue(all(r.isError() == False for r in results))  # check the results errorness (or you can check r.severity < 0 for error)
    self.assertEqual(alarm.sense(), False)  # does the same as the line above - just a sanity check    

    # don't forget to unpin the date at the end
    self.unpinDateTime()