Skip to content

Commit a5c8ac4

Browse files
authored
Merge pull request #7339 from fstagni/80_fixes76
[8.0] Fix for sending notifications for expiring proxies
2 parents e921ca5 + 7a99e7c commit a5c8ac4

File tree

4 files changed

+63
-135
lines changed

4 files changed

+63
-135
lines changed

src/DIRAC/FrameworkSystem/Client/NotificationClient.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
""" DIRAC Notification Client class encapsulates the methods exposed
22
by the Notification service.
33
"""
4-
from DIRAC import gLogger, S_ERROR
4+
from DIRAC import S_ERROR, gLogger
55
from DIRAC.Core.Base.Client import Client, createClient
66
from DIRAC.Core.Utilities.Mail import Mail
77

src/DIRAC/FrameworkSystem/DB/NotificationDB.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
"""
33
import time
44

5-
from DIRAC import gConfig, gLogger, S_OK, S_ERROR
6-
from DIRAC.Core.Utilities.Mail import Mail
5+
from DIRAC import S_ERROR, S_OK, gConfig, gLogger
6+
from DIRAC.ConfigurationSystem.Client.Helpers import Registry
77
from DIRAC.Core.Base.DB import DB
88
from DIRAC.Core.Utilities import DEncode
9-
from DIRAC.ConfigurationSystem.Client.Helpers import Registry
9+
from DIRAC.Core.Utilities.Mail import Mail
1010

1111

1212
class NotificationDB(DB):

src/DIRAC/FrameworkSystem/DB/ProxyDB.py

Lines changed: 55 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,21 @@
1111
* ProxyDB_VOMSProxies -- proxy storage table with VOMS extension already added.
1212
* ProxyDB_Log -- table with logs.
1313
* ProxyDB_Tokens -- token storage table for proxy requests.
14-
* ProxyDB_ExpNotifs -- a table for accumulating proxy expiration notifications.
1514
"""
16-
import time
17-
import random
1815
import hashlib
16+
import random
17+
import textwrap
18+
import time
1919
from urllib.request import urlopen
2020

21-
from DIRAC import gConfig, gLogger, S_OK, S_ERROR
21+
from DIRAC import S_ERROR, S_OK, gConfig, gLogger
22+
from DIRAC.ConfigurationSystem.Client.Helpers import Registry
2223
from DIRAC.Core.Base.DB import DB
23-
from DIRAC.Core.Utilities import DErrno
24-
from DIRAC.Core.Security import Properties
25-
from DIRAC.Core.Security.VOMS import VOMS
2624
from DIRAC.Core.Security.MyProxy import MyProxy
27-
from DIRAC.Core.Security.X509Request import X509Request # pylint: disable=import-error
25+
from DIRAC.Core.Security.VOMS import VOMS
2826
from DIRAC.Core.Security.X509Chain import X509Chain, isPUSPdn # pylint: disable=import-error
29-
from DIRAC.ConfigurationSystem.Client.Helpers import Registry
27+
from DIRAC.Core.Security.X509Request import X509Request # pylint: disable=import-error
28+
from DIRAC.Core.Utilities import DErrno
3029
from DIRAC.FrameworkSystem.Client.NotificationClient import NotificationClient
3130
from DIRAC.Resources.ProxyProvider.ProxyProviderFactory import ProxyProviderFactory
3231

@@ -157,17 +156,6 @@ def __initializeDB(self):
157156
"PrimaryKey": "Token",
158157
}
159158

160-
if "ProxyDB_ExpNotifs" not in tablesInDB:
161-
tablesD["ProxyDB_ExpNotifs"] = {
162-
"Fields": {
163-
"UserDN": "VARCHAR(255) NOT NULL",
164-
"UserGroup": "VARCHAR(255) NOT NULL",
165-
"LifeLimit": "INTEGER UNSIGNED DEFAULT 0",
166-
"ExpirationTime": "DATETIME NOT NULL",
167-
},
168-
"PrimaryKey": ["UserDN", "UserGroup"],
169-
}
170-
171159
return self._createTables(tablesD)
172160

173161
def __addUserNameToTable(self, tableName):
@@ -480,8 +468,7 @@ def purgeExpiredProxies(self, sendNotifications=True):
480468
purged += result["Value"]
481469
self.log.info(f"Purged {result['Value']} expired proxies from {tableName}")
482470
if sendNotifications:
483-
result = self.sendExpirationNotifications()
484-
if not result["OK"]:
471+
if not (result := self.sendExpirationNotifications())["OK"]:
485472
return result
486473
return S_OK(purged)
487474

@@ -1347,134 +1334,76 @@ def useToken(self, token, requesterDN, requesterGroup):
13471334
return result
13481335
return S_OK(result["Value"] > 0)
13491336

1350-
def __cleanExpNotifs(self):
1351-
"""Clean expired notifications from the db
1352-
1353-
:return: S_OK()/S_ERROR()
1354-
"""
1355-
cmd = "DELETE FROM `ProxyDB_ExpNotifs` WHERE ExpirationTime < UTC_TIMESTAMP()"
1356-
return self._update(cmd)
1357-
1358-
# FIXME: Add clean proxy
13591337
def sendExpirationNotifications(self):
13601338
"""Send notification about expiration
13611339
13621340
:return: S_OK(list)/S_ERROR() -- tuple list of user DN, group and proxy left time
13631341
"""
1364-
result = self.__cleanExpNotifs()
1365-
if not result["OK"]:
1366-
return result
1367-
cmd = "SELECT UserDN, UserGroup, LifeLimit FROM `ProxyDB_ExpNotifs`"
1368-
result = self._query(cmd)
1369-
if not result["OK"]:
1370-
return result
1371-
notifDone = {(row[0], row[1]): row[2] for row in result["Value"]}
13721342
notifLimits = sorted(int(x) for x in self.getCSOption("NotificationTimes", ProxyDB.NOTIFICATION_TIMES))
1373-
sqlSel = "UserDN, UserGroup, TIMESTAMPDIFF( SECOND, UTC_TIMESTAMP(), ExpirationTime )"
1374-
sqlCond = "TIMESTAMPDIFF( SECOND, UTC_TIMESTAMP(), ExpirationTime ) < %d" % max(notifLimits)
1375-
cmd = f"SELECT {sqlSel} FROM `ProxyDB_Proxies` WHERE {sqlCond}"
1376-
result = self._query(cmd)
1377-
if not result["OK"]:
1378-
return result
1379-
pilotProps = (Properties.GENERIC_PILOT, Properties.PILOT)
1380-
data = result["Value"]
1381-
sent = []
1382-
for row in data:
1383-
userDN, group, lTime = row
1384-
# If it's a pilot proxy, skip it
1385-
if Registry.groupHasProperties(group, pilotProps):
1386-
continue
1387-
# IF it dosn't hace the auto upload proxy, skip it
1388-
if not Registry.getGroupOption(group, "AutoUploadProxy", False):
1389-
continue
1390-
notKey = (userDN, group)
1391-
for notifLimit in notifLimits:
1392-
if notifLimit < lTime:
1393-
# Not yet in this notification limit
1394-
continue
1395-
if notKey in notifDone and notifDone[notKey] <= notifLimit:
1396-
# Already notified for this notification limit
1397-
break
1398-
if not self._notifyProxyAboutToExpire(userDN, lTime):
1399-
# Cannot send notification, retry later
1343+
1344+
for notifLimit in notifLimits:
1345+
sqlSel = "UserName, TIMESTAMPDIFF( SECOND, UTC_TIMESTAMP(), ExpirationTime )"
1346+
sqlCond = "TIMESTAMPDIFF( SECOND, UTC_TIMESTAMP(), ExpirationTime ) < %d" % notifLimit
1347+
sqlCond += " AND TIMESTAMPDIFF( SECOND, UTC_TIMESTAMP(), ExpirationTime ) > %d" % (notifLimit - 86400)
1348+
1349+
if not (result := self._query(f"SELECT {sqlSel} FROM `ProxyDB_CleanProxies` WHERE {sqlCond}"))["OK"]:
1350+
return result
1351+
for userName, lTime in result["Value"]:
1352+
if not self._notifyProxyAboutToExpire(userName, lTime):
1353+
self.log.warn("Cannot send notification, will retry later")
14001354
break
1401-
try:
1402-
sUserDN = self._escapeString(userDN)["Value"]
1403-
sGroup = self._escapeString(group)["Value"]
1404-
except KeyError:
1405-
return S_ERROR("OOPS")
1406-
if notKey not in notifDone:
1407-
values = "( %s, %s, %d, TIMESTAMPADD( SECOND, %s, UTC_TIMESTAMP() ) )" % (
1408-
sUserDN,
1409-
sGroup,
1410-
notifLimit,
1411-
lTime,
1412-
)
1413-
cmd = (
1414-
"INSERT INTO `ProxyDB_ExpNotifs` ( UserDN, UserGroup, LifeLimit, ExpirationTime ) VALUES %s"
1415-
% values
1416-
)
1417-
result = self._update(cmd)
1418-
if not result["OK"]:
1419-
gLogger.error("Could not mark notification as sent", result["Message"])
1420-
else:
1421-
values = "LifeLimit = %d, ExpirationTime = TIMESTAMPADD( SECOND, %s, UTC_TIMESTAMP() )" % (
1422-
notifLimit,
1423-
lTime,
1424-
)
1425-
cmd = "UPDATE `ProxyDB_ExpNotifs` SET {} WHERE UserDN = {} AND UserGroup = {}".format(
1426-
values,
1427-
sUserDN,
1428-
sGroup,
1429-
)
1430-
result = self._update(cmd)
1431-
if not result["OK"]:
1432-
gLogger.error("Could not mark notification as sent", result["Message"])
1433-
sent.append((userDN, group, lTime))
1434-
notifDone[notKey] = notifLimit
1435-
return S_OK(sent)
1436-
1437-
def _notifyProxyAboutToExpire(self, userDN, lTime):
1355+
1356+
return S_OK()
1357+
1358+
def _notifyProxyAboutToExpire(self, userName, lTime):
14381359
"""Send notification mail about to expire
14391360
1440-
:param str userDN: user DN
1441-
:param int lTime: left proxy live time in a seconds
1361+
:param str userName: user name
1362+
:param int lTime: left proxy live time in seconds
14421363
14431364
:return: boolean
14441365
"""
1445-
result = Registry.getUsernameForDN(userDN)
1446-
if not result["OK"]:
1447-
return False
1448-
userName = result["Value"]
1449-
userEMail = Registry.getUserOption(userName, "Email", "")
1450-
if not userEMail:
1366+
if not (userEMail := Registry.getUserOption(userName, "Email")):
14511367
gLogger.error("Could not discover user email", userName)
14521368
return False
1369+
if not (userDN := Registry.getUserOption(userName, "DN")):
1370+
gLogger.error("Could not discover user DN", userName)
1371+
return False
14531372
daysLeft = int(lTime / 86400)
14541373
if daysLeft <= 0:
14551374
return True # Don't annoy users with -1 messages
14561375
msgSubject = "Your proxy uploaded to DIRAC will expire in %d days" % daysLeft
1457-
msgBody = """\
1458-
Dear %s,
1376+
msgBody = textwrap.dedent(
1377+
"""\
1378+
Dear %s,
1379+
1380+
The proxy you uploaded to DIRAC will expire in aproximately %d days. The proxy
1381+
information is:
1382+
1383+
DN: %s
14591384
1460-
The proxy you uploaded to DIRAC will expire in aproximately %d days. The proxy
1461-
information is:
1385+
If you plan on keep using this credentials, please upload a newer proxy by executing:
14621386
1463-
DN: %s
1387+
$ dirac-proxy-init
14641388
1465-
If you have been issued different certificate, please make sure you have a
1466-
proxy uploaded with that certificate.
1389+
If you have been issued different certificate, please make sure you have a
1390+
proxy uploaded with that certificate.
14671391
1468-
Cheers,
1469-
DIRAC's Proxy Manager
1470-
""" % (
1471-
userName,
1472-
daysLeft,
1473-
userDN,
1392+
Cheers,
1393+
DIRAC's Proxy Manager
1394+
"""
1395+
% (
1396+
userName,
1397+
daysLeft,
1398+
userDN,
1399+
)
14741400
)
14751401
fromAddr = self._mailFrom
1476-
result = self.__notifClient.sendMail(userEMail, msgSubject, msgBody, fromAddress=fromAddr)
1477-
if not result["OK"]:
1402+
if not (
1403+
result := self.__notifClient.sendMail(
1404+
userEMail, msgSubject, msgBody, fromAddress=fromAddr, localAttempt=False
1405+
)
1406+
)["OK"]:
14781407
gLogger.error("Could not send email", result["Message"])
14791408
return False
14801409
return True

src/DIRAC/FrameworkSystem/Service/NotificationHandler.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,14 @@
1212
It can also be used to set alarms to be promptly forwarded to those
1313
subscribing to them.
1414
"""
15-
from DIRAC import gConfig, S_OK, S_ERROR
16-
15+
from DIRAC import S_ERROR, S_OK, gConfig
16+
from DIRAC.ConfigurationSystem.Client import PathFinder
1717
from DIRAC.Core.DISET.RequestHandler import RequestHandler
18+
from DIRAC.Core.Security import Properties
19+
from DIRAC.Core.Utilities.DictCache import DictCache
1820
from DIRAC.Core.Utilities.Mail import Mail
1921
from DIRAC.Core.Utilities.ThreadScheduler import gThreadScheduler
20-
from DIRAC.Core.Security import Properties
21-
from DIRAC.ConfigurationSystem.Client import PathFinder
2222
from DIRAC.FrameworkSystem.DB.NotificationDB import NotificationDB
23-
from DIRAC.Core.Utilities.DictCache import DictCache
2423

2524

2625
class NotificationHandler(RequestHandler):

0 commit comments

Comments
 (0)