From ae13429081309fcd77dc727455bb01a79cec269d Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=2E=C3=87a=C4=9Flar=20Onur?= Date: Thu, 2 Dec 2010 15:00:28 -0500 Subject: [PATCH] add notification support --- aspects/ratelimitaspects.py | 72 ++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/aspects/ratelimitaspects.py b/aspects/ratelimitaspects.py index 84af023..e95764f 100644 --- a/aspects/ratelimitaspects.py +++ b/aspects/ratelimitaspects.py @@ -6,12 +6,19 @@ from PLC.Config import Config from PLC.Faults import PLCPermissionDenied +from PLC.Nodes import Node, Nodes +from PLC.Persons import Person, Persons +from PLC.Sessions import Session, Sessions + from datetime import datetime, timedelta from pyaspects.meta import MetaAspect import memcache +import os +import sys + class BaseRateLimit(object): def __init__(self): @@ -31,6 +38,35 @@ class BaseRateLimit(object): log.write("%s - %s\n" % (date, line)) log.flush() + def mail(self, to): + sendmail = os.popen("/usr/sbin/sendmail -N never -t -f%s" % self.config.PLC_MAIL_SUPPORT_ADDRESS, "w") + + subject = "[PLCAPI] Maximum allowed number of API calls exceeded" + + header = {'from': "%s Support <%s>" % (self.config.PLC_NAME, self.config.PLC_MAIL_SUPPORT_ADDRESS), + 'to': "%s, %s" % (to, self.config.PLC_MAIL_SUPPORT_ADDRESS), + 'version': sys.version.split(" ")[0], + 'subject': subject} + + body = "Maximum allowed number of API calls exceeded for the user %s within the last %s minutes." % (to, self.minutes) + + # Write headers + sendmail.write( +""" +Content-type: text/plain +From: %(from)s +Reply-To: %(from)s +To: %(to)s +X-Mailer: Python/%(version)s +Subject: %(subject)s + +""".lstrip() % header) + + # Write body + sendmail.write(body) + # Done + sendmail.close() + def before(self, wobj, data, *args, **kwargs): # ratelimit_128.112.139.115_201011091532 = 1 # ratelimit_128.112.139.115_201011091533 = 14 @@ -46,8 +82,17 @@ class BaseRateLimit(object): except: return + # decode api_method_caller if api_method == "session": - api_method_caller = args[0]["session"] + api_method_caller = Sessions(wobj.api, {'session_id': args[0]["session"]}) + if api_method_caller == []: + return + elif api_method_caller[0]["person_id"] != None: + api_method_caller = Persons(wobj.api, api_method_caller[0]["person_id"])[0]["email"] + elif api_method_caller[0]["node_id"] != None: + api_method_caller = Nodes(wobj.api, api_method_caller[0]["node_id"])[0]["hostname"] + else: + api_method_caller = args[0]["session"] elif api_method == "password" or api_method == "capability": api_method_caller = args[0]["Username"] elif api_method == "gpg": @@ -59,34 +104,43 @@ class BaseRateLimit(object): else: api_method_caller = "unknown" + # excludes if api_method_source == None or api_method_source[0] == self.config.PLC_API_IP or api_method_source[0] in self.whitelist: return + # sanity check if api_method_caller == None: self.log("%s called from %s with Username = None?" % (api_method_name, api_method_source[0])) return + # normalize unicode string otherwise memcache throws an exception + api_method_caller = str(api_method_caller) + mc = memcache.Client(["%s:11211" % self.config.PLC_API_HOST]) now = datetime.now() - current_key = "%s_%s_%s_%s" % (self.prefix, api_method_caller, api_method_source[0], now.strftime("%Y%m%d%H%M")) + current_key = "%s_%s_%s_%s" % (self.prefix, api_method_caller, api_method_source[0], now.strftime("%Y%m%d%H%M")) keys_to_check = ["%s_%s_%s_%s" % (self.prefix, api_method_caller, api_method_source[0], (now - timedelta(minutes = minute)).strftime("%Y%m%d%H%M")) for minute in range(self.minutes + 1)] try: - value = mc.incr(current_key) + mc.incr(current_key) except ValueError: - value = None - - if value == None: mc.set(current_key, 1, time=self.expire_after) - result = mc.get_multi(keys_to_check) + results = mc.get_multi(keys_to_check) total_requests = 0 - for i in result: - total_requests += result[i] + for i in results: + total_requests += results[i] if total_requests > self.requests: self.log("%s - %s" % (api_method_source[0], api_method_caller)) + + caller_key = "%s_%s" % (self.prefix, api_method_caller) + if mc.get(caller_key) == None: + mc.set(caller_key, 1, time = self.expire_after) + if (api_method == "session" and api_method_caller.__contains__("@")) or (api_method == "password" or api_method == "capability"): + self.mail(api_method_caller) + raise PLCPermissionDenied, "Maximum allowed number of API calls exceeded" def after(self, wobj, data, *args, **kwargs): -- 2.43.0