From 14202d05b305f48d21691391260c910db546f814 Mon Sep 17 00:00:00 2001
From: Sebastien Robin <seb@nexedi.com>
Date: Wed, 11 Jul 2007 17:20:09 +0000
Subject: [PATCH] allow to define wich node starts alarms on a cluster

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@15197 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Tool/AlarmTool.py         |  81 +++++++++++++----
 product/ERP5/dtml/manageAlarmNode.dtml | 117 +++++++++++++++++++++++++
 2 files changed, 180 insertions(+), 18 deletions(-)
 create mode 100755 product/ERP5/dtml/manageAlarmNode.dtml

diff --git a/product/ERP5/Tool/AlarmTool.py b/product/ERP5/Tool/AlarmTool.py
index ac3d82658d..a0489f85d6 100644
--- a/product/ERP5/Tool/AlarmTool.py
+++ b/product/ERP5/Tool/AlarmTool.py
@@ -37,9 +37,15 @@ from Products.ERP5Type.Tool.BaseTool import BaseTool
 from Products.ERP5Type import Permissions
 from Products.ERP5 import _dtmldir
 from DateTime import DateTime
+import urllib
+import socket
 
 from zLOG import LOG, INFO
 
+import re
+# minimal IP:Port regexp
+NODE_RE = re.compile('^\d+\.\d+\.\d+\.\d+:\d+$')
+
 try:
   from Products.TimerService import getTimerService
 except ImportError:
@@ -68,21 +74,15 @@ class AlarmTool(BaseTool):
   security.declareProtected( Permissions.ManagePortal, 'manage_overview' )
   manage_overview = DTMLFile( 'explainAlarmTool', _dtmldir )
 
-  security.declareProtected( Permissions.ManagePortal , 'manageAlarmList' )
-  manageAlarmList = DTMLFile( 'manageAlarmList', _dtmldir )
-
-  security.declareProtected( Permissions.ManagePortal , 'manageAlarmAdvanced' )
-  manageAlarmAdvanced = DTMLFile( 'manageAlarmAdvanced', _dtmldir )
+  security.declareProtected( Permissions.ManagePortal , 'manageAlarmNode' )
+  manageAlarmNode = DTMLFile( 'manageAlarmNode', _dtmldir )
 
 
   manage_options = ( ( { 'label'   : 'Overview'
                        , 'action'   : 'manage_overview'
                        }
-                     , { 'label'   : 'All Alarms'
-                       , 'action'   : 'manageAlarmList'
-                       }
-                     , { 'label'   : 'Advanced'
-                       , 'action'   : 'manageAlarmAdvanced'
+                     , { 'label'   : 'Alarm Node'
+                       , 'action'   : 'manageAlarmNode'
                        }
                      ,
                      )
@@ -91,6 +91,7 @@ class AlarmTool(BaseTool):
 
   _properties = ( {'id': 'interval', 'type': 'int', 'mode': 'w', }, )
   interval = 60 # Default interval for alarms is 60 seconds
+  alarmNode = ''
 
   # API to manage alarms
   # Aim of this API:
@@ -192,12 +193,56 @@ class AlarmTool(BaseTool):
       This method is called by TimerService in the interval given
       in zope.conf. The Default is every 5 seconds.
     """
-    global last_tic
-    last_tic_lock.acquire(1)
-    try:
-      if tick.timeTime() - last_tic >= self.interval:
-        self.tic()
-        last_tic = tick.timeTime()
-    finally:
-      last_tic_lock.release()
+    # only start when we are the alarmNode or if it's empty
+    if (self.alarmNode == self.getCurrentNode()) \
+      or not self.alarmNode:
+      global last_tic
+      last_tic_lock.acquire(1)
+      try:
+        if tick.timeTime() - last_tic >= self.interval:
+          self.tic()
+          last_tic = tick.timeTime()
+      finally:
+        last_tic_lock.release()
+
+  def getCurrentNode(self):
+      """ Return current node in form ip:port """
+      port = ''
+      from asyncore import socket_map
+      for k, v in socket_map.items():
+          if hasattr(v, 'port'):
+              # see Zope/lib/python/App/ApplicationManager.py: def getServers(self)
+              type = str(getattr(v, '__class__', 'unknown'))
+              if type == 'ZServer.HTTPServer.zhttp_server':
+                  port = v.port
+                  break
+      ip = socket.gethostbyname(socket.gethostname())
+      currentNode = '%s:%s' %(ip, port)
+      return currentNode
+      
+  security.declarePublic('getAlarmNode')
+  def getAlarmNode(self):
+      """ Return the alarmNode """
+      return self.alarmNode
+
+  def _isValidNodeName(self, node_name) :
+    """Check we have been provided a good node name"""
+    return isinstance(node_name, str) and NODE_RE.match(node_name)
+      
+  security.declarePublic('manage_setAlarmNode')
+  def manage_setAlarmNode(self, alarmNode, REQUEST=None):
+      """ set the alarm node """   
+      if not alarmNode or self._isValidNodeName(alarmNode):
+        self.alarmNode = alarmNode
+        if REQUEST is not None:
+            REQUEST.RESPONSE.redirect(
+                REQUEST.URL1 +
+                '/manageAlarmNode?manage_tabs_message=' +
+                urllib.quote("Distributing Node successfully changed."))
+      else :
+        if REQUEST is not None:
+            REQUEST.RESPONSE.redirect(
+                REQUEST.URL1 +
+                '/manageAlarmNode?manage_tabs_message=' +
+                urllib.quote("Malformed Distributing Node."))
 
diff --git a/product/ERP5/dtml/manageAlarmNode.dtml b/product/ERP5/dtml/manageAlarmNode.dtml
new file mode 100755
index 0000000000..3a1149f27c
--- /dev/null
+++ b/product/ERP5/dtml/manageAlarmNode.dtml
@@ -0,0 +1,117 @@
+<dtml-comment>
+Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
+                    Sebastien Robin <seb@nexedi.com>
+
+WARNING: This program as such is intended to be used by professional
+programmers who take the whole responsability of assessing all potential
+consequences resulting from its eventual inadequacies and bugs
+
+This program as such is not intended to be used by end users. End
+users who are looking for a ready-to-use solution with commercial
+garantees and support are strongly adviced to contract a Free Software
+Service Company
+
+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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+</dtml-comment>
+
+<dtml-var manage_page_header>
+<dtml-var manage_tabs>
+
+<p class="form-help">
+  This tab defines the node wich starts Alarms.
+  To provide high performance, alarms should start a script wich only start
+  some activities. Like this, the activity tool will be in charge to distribute
+  the work on many nodes.
+
+  The format to adress a node in this configuration is therfore
+  <code>IP-Address</code>:<code>Port-Number</code>.
+</p>
+
+<p class="form-help">
+  The interval of distribution and activation of the activities is per default 5 seconds. This can be
+  changed by altering the value of <i>interval</i> in the section <i>time-server</i> in the
+  Zope Configuration File <i>zope.conf</i>.
+</p>
+
+<p class="form-help">
+    The <i>Alarm node</i> is responsible for the start of Alarms. The default value is empty
+    which means that every existing node will try to do launch of alarms, which is fine when there
+    is only one ZServer running.
+    To change the Alarm Node, edit the value and click &quot;Change&quot;.
+</p>
+
+<form action="&dtml-URL1;">
+
+<table cellspacing="0" cellpadding="2" border="0" width="100%">
+  <tr class="list-header">
+    <td align="left" valign="top" colspan=2>
+      <div class="form-label">Alarm Node</div>
+    </td>
+  </tr>
+  <tr>
+    <td>
+      <div class="form-label">IP:Port</div>
+    </td>
+    <td align="left">
+      <div class="form-item">
+        <input type="text" name="alarmNode" value="&dtml-getAlarmNode;" size="19" />
+      </div>
+    </td>
+  </tr>
+  <tr>
+    <td>&nbsp;</td>
+    <td align="left">
+      <div class="form-element">
+        <input type="submit" class="form-element" name="manage_setAlarmNode:method" value=" Change ">
+     </div>
+    </td>
+  </tr>
+  <tr>
+    <td>&nbsp;</td>
+    <td>&nbsp;</td>
+  </tr>
+  <tr>
+    <td align="left" colspan=2>
+      <p class="form-help">
+        Subscribe/Unsubscribe from Timer Service
+      </p>
+    </td>
+  </tr>
+  <tr class="list-header">
+    <td align="left" valign="top" colspan=2>
+      <div class="form-label">
+        Status:
+        <dtml-if isSubscribed>
+          Subscribed
+        <dtml-else>
+          Not Subscribed
+       </dtml-if>
+      </div>
+    </td>
+  </tr>
+  <tr>
+    <td align="left">
+      <div class="form-element">
+        <dtml-if isSubscribed>
+          <input type="submit" class="form-element" name="unsubscribe:method" value=" Unsubscribe ">
+        <dtml-else>
+          <input type="submit" class="form-element" name="subscribe:method" value=" Subscribe ">
+        </dtml-if>
+     </div>
+    </td>
+  </tr>
+</table>
+</form>
+<dtml-var manage_page_footer>
-- 
2.30.9