<s> """<STR_LIT>""" <EOL> import copy <EOL> import httplib <EOL> import json <EOL> import math <EOL> import re <EOL> import time <EOL> import urllib <EOL> import webob . exc <EOL> from xml . dom import minidom <EOL> from collections import defaultdict <EOL> from webob . dec import wsgify <EOL> from nova import quota <EOL> from nova import utils <EOL> from nova import wsgi as base_wsgi <EOL> from nova . api . openstack import common <EOL> from nova . api . openstack import faults <EOL> from nova . api . openstack . views import limits as limits_views <EOL> from nova . api . openstack import wsgi <EOL> PER_SECOND = <NUM_LIT:1> <EOL> PER_MINUTE = <NUM_LIT> <EOL> PER_HOUR = <NUM_LIT> * <NUM_LIT> <EOL> PER_DAY = <NUM_LIT> * <NUM_LIT> * <NUM_LIT> <EOL> class LimitsController ( object ) : <EOL> """<STR_LIT>""" <EOL> def index ( self , req ) : <EOL> """<STR_LIT>""" <EOL> context = req . environ [ '<STR_LIT>' ] <EOL> abs_limits = quota . get_project_quotas ( context , context . project_id ) <EOL> rate_limits = req . environ . get ( "<STR_LIT>" , [ ] ) <EOL> builder = self . _get_view_builder ( req ) <EOL> return builder . build ( rate_limits , abs_limits ) <EOL> def _get_view_builder ( self , req ) : <EOL> raise NotImplementedError ( ) <EOL> class LimitsControllerV10 ( LimitsController ) : <EOL> def _get_view_builder ( self , req ) : <EOL> return limits_views . ViewBuilderV10 ( ) <EOL> class LimitsControllerV11 ( LimitsController ) : <EOL> def _get_view_builder ( self , req ) : <EOL> return limits_views . ViewBuilderV11 ( ) <EOL> class LimitsXMLSerializer ( wsgi . XMLDictSerializer ) : <EOL> xmlns = wsgi . XMLNS_V11 <EOL> def __init__ ( self ) : <EOL> pass <EOL> def _create_rates_node ( self , xml_doc , rates ) : <EOL> rates_node = xml_doc . createElement ( '<STR_LIT>' ) <EOL> for rate in rates : <EOL> rate_node = xml_doc . createElement ( '<STR_LIT>' ) <EOL> rate_node . setAttribute ( '<STR_LIT>' , rate [ '<STR_LIT>' ] ) <EOL> rate_node . setAttribute ( '<STR_LIT>' , rate [ '<STR_LIT>' ] ) <EOL> for limit in rate [ '<STR_LIT>' ] : <EOL> limit_node = xml_doc . createElement ( '<STR_LIT>' ) <EOL> limit_node . setAttribute ( '<STR_LIT:value>' , str ( limit [ '<STR_LIT:value>' ] ) ) <EOL> limit_node . setAttribute ( '<STR_LIT>' , limit [ '<STR_LIT>' ] ) <EOL> limit_node . setAttribute ( '<STR_LIT>' , str ( limit [ '<STR_LIT>' ] ) ) <EOL> limit_node . setAttribute ( '<STR_LIT>' , limit [ '<STR_LIT>' ] ) <EOL> limit_node . setAttribute ( '<STR_LIT>' , <EOL> str ( limit [ '<STR_LIT>' ] ) ) <EOL> rate_node . appendChild ( limit_node ) <EOL> rates_node . appendChild ( rate_node ) <EOL> return rates_node <EOL> def _create_absolute_node ( self , xml_doc , absolutes ) : <EOL> absolute_node = xml_doc . createElement ( '<STR_LIT>' ) <EOL> for key , value in absolutes . iteritems ( ) : <EOL> limit_node = xml_doc . createElement ( '<STR_LIT>' ) <EOL> limit_node . setAttribute ( '<STR_LIT:name>' , key ) <EOL> limit_node . setAttribute ( '<STR_LIT:value>' , str ( value ) ) <EOL> absolute_node . appendChild ( limit_node ) <EOL> return absolute_node <EOL> def _limits_to_xml ( self , xml_doc , limits ) : <EOL> limits_node = xml_doc . createElement ( '<STR_LIT>' ) <EOL> rates_node = self . _create_rates_node ( xml_doc , limits [ '<STR_LIT>' ] ) <EOL> limits_node . appendChild ( rates_node ) <EOL> absolute_node = self . _create_absolute_node ( xml_doc , limits [ '<STR_LIT>' ] ) <EOL> limits_node . appendChild ( absolute_node ) <EOL> return limits_node <EOL> def index ( self , limits_dict ) : <EOL> xml_doc = minidom . Document ( ) <EOL> node = self . _limits_to_xml ( xml_doc , limits_dict [ '<STR_LIT>' ] ) <EOL> return self . to_xml_string ( node , False ) <EOL> def create_resource ( version = '<STR_LIT:1.0>' ) : <EOL> controller = { <EOL> '<STR_LIT:1.0>' : LimitsControllerV10 , <EOL> '<STR_LIT>' : LimitsControllerV11 , <EOL> } [ version ] ( ) <EOL> xmlns = { <EOL> '<STR_LIT:1.0>' : wsgi . XMLNS_V10 , <EOL> '<STR_LIT>' : wsgi . XMLNS_V11 , <EOL> } [ version ] <EOL> metadata = { <EOL> "<STR_LIT>" : { <EOL> "<STR_LIT>" : [ "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT:value>" , "<STR_LIT>" , <EOL> "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT:name>" ] , <EOL> } , <EOL> "<STR_LIT>" : { <EOL> "<STR_LIT>" : "<STR_LIT>" , <EOL> } , <EOL> } <EOL> xml_serializer = { <EOL> '<STR_LIT:1.0>' : wsgi . XMLDictSerializer ( xmlns = xmlns , metadata = metadata ) , <EOL> '<STR_LIT>' : LimitsXMLSerializer ( ) , <EOL> } [ version ] <EOL> body_serializers = { <EOL> '<STR_LIT>' : xml_serializer , <EOL> } <EOL> serializer = wsgi . ResponseSerializer ( body_serializers ) <EOL> return wsgi . Resource ( controller , serializer = serializer ) <EOL> class Limit ( object ) : <EOL> """<STR_LIT>""" <EOL> UNITS = { <EOL> <NUM_LIT:1> : "<STR_LIT>" , <EOL> <NUM_LIT> : "<STR_LIT>" , <EOL> <NUM_LIT> * <NUM_LIT> : "<STR_LIT>" , <EOL> <NUM_LIT> * <NUM_LIT> * <NUM_LIT> : "<STR_LIT>" , <EOL> } <EOL> UNIT_MAP = dict ( [ ( v , k ) for k , v in UNITS . items ( ) ] ) <EOL> def __init__ ( self , verb , uri , regex , value , unit ) : <EOL> """<STR_LIT>""" <EOL> self . verb = verb <EOL> self . uri = uri <EOL> self . regex = regex <EOL> self . value = int ( value ) <EOL> self . unit = unit <EOL> self . unit_string = self . display_unit ( ) . lower ( ) <EOL> self . remaining = int ( value ) <EOL> if value <= <NUM_LIT:0> : <EOL> raise ValueError ( "<STR_LIT>" ) <EOL> self . last_request = None <EOL> self . next_request = None <EOL> self . water_level = <NUM_LIT:0> <EOL> self . capacity = self . unit <EOL> self . request_value = float ( self . capacity ) / float ( self . value ) <EOL> self . error_message = _ ( "<STR_LIT>" "<STR_LIT>" % self . __dict__ ) <EOL> def __call__ ( self , verb , url ) : <EOL> """<STR_LIT>""" <EOL> if self . verb != verb or not re . match ( self . regex , url ) : <EOL> return <EOL> now = self . _get_time ( ) <EOL> if self . last_request is None : <EOL> self . last_request = now <EOL> leak_value = now - self . last_request <EOL> self . water_level -= leak_value <EOL> self . water_level = max ( self . water_level , <NUM_LIT:0> ) <EOL> self . water_level += self . request_value <EOL> difference = self . water_level - self . capacity <EOL> self . last_request = now <EOL> if difference > <NUM_LIT:0> : <EOL> self . water_level -= self . request_value <EOL> self . next_request = now + difference <EOL> return difference <EOL> cap = self . capacity <EOL> water = self . water_level <EOL> val = self . value <EOL> self . remaining = math . floor ( ( ( cap - water ) / cap ) * val ) <EOL> self . next_request = now <EOL> def _get_time ( self ) : <EOL> """<STR_LIT>""" <EOL> return time . time ( ) <EOL> def display_unit ( self ) : <EOL> """<STR_LIT>""" <EOL> return self . UNITS . get ( self . unit , "<STR_LIT>" ) <EOL> def display ( self ) : <EOL> """<STR_LIT>""" <EOL> return { <EOL> "<STR_LIT>" : self . verb , <EOL> "<STR_LIT>" : self . uri , <EOL> "<STR_LIT>" : self . regex , <EOL> "<STR_LIT:value>" : self . value , <EOL> "<STR_LIT>" : int ( self . remaining ) , <EOL> "<STR_LIT>" : self . display_unit ( ) , <EOL> "<STR_LIT>" : int ( self . next_request or self . _get_time ( ) ) , <EOL> } <EOL> DEFAULT_LIMITS = [ <EOL> Limit ( "<STR_LIT:POST>" , "<STR_LIT:*>" , "<STR_LIT>" , <NUM_LIT:10> , PER_MINUTE ) , <EOL> Limit ( "<STR_LIT:POST>" , "<STR_LIT>" , "<STR_LIT>" , <NUM_LIT:50> , PER_DAY ) , <EOL> Limit ( "<STR_LIT>" , "<STR_LIT:*>" , "<STR_LIT>" , <NUM_LIT:10> , PER_MINUTE ) , <EOL> Limit ( "<STR_LIT:GET>" , "<STR_LIT>" , "<STR_LIT>" , <NUM_LIT:3> , PER_MINUTE ) , <EOL> Limit ( "<STR_LIT>" , "<STR_LIT:*>" , "<STR_LIT>" , <NUM_LIT:100> , PER_MINUTE ) , <EOL> ] <EOL> class RateLimitingMiddleware ( base_wsgi . Middleware ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , application , limits = None , limiter = None , ** kwargs ) : <EOL> """<STR_LIT>""" <EOL> base_wsgi . Middleware . __init__ ( self , application ) <EOL> if limiter is None : <EOL> limiter = Limiter <EOL> else : <EOL> limiter = utils . import_class ( limiter ) <EOL> if limits is not None : <EOL> limits = limiter . parse_limits ( limits ) <EOL> self . _limiter = limiter ( limits or DEFAULT_LIMITS , ** kwargs ) <EOL> @ wsgify ( RequestClass = wsgi . Request ) <EOL> def __call__ ( self , req ) : <EOL> """<STR_LIT>""" <EOL> verb = req . method <EOL> url = req . url <EOL> context = req . environ . get ( "<STR_LIT>" ) <EOL> if context : <EOL> username = context . user_id <EOL> else : <EOL> username = None <EOL> delay , error = self . _limiter . check_for_delay ( verb , url , username ) <EOL> if delay : <EOL> msg = _ ( "<STR_LIT>" ) <EOL> retry = time . time ( ) + delay <EOL> return faults . OverLimitFault ( msg , error , retry ) <EOL> req . environ [ "<STR_LIT>" ] = self . _limiter . get_limits ( username ) <EOL> return self . application <EOL> class Limiter ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , limits , ** kwargs ) : <EOL> """<STR_LIT>""" <EOL> self . limits = copy . deepcopy ( limits ) <EOL> self . levels = defaultdict ( lambda : copy . deepcopy ( limits ) ) <EOL> for key , value in kwargs . items ( ) : <EOL> if key . startswith ( '<STR_LIT>' ) : <EOL> username = key [ <NUM_LIT:5> : ] <EOL> self . levels [ username ] = self . parse_limits ( value ) <EOL> def get_limits ( self , username = None ) : <EOL> """<STR_LIT>""" <EOL> return [ limit . display ( ) for limit in self . levels [ username ] ] <EOL> def check_for_delay ( self , verb , url , username = None ) : <EOL> """<STR_LIT>""" <EOL> delays = [ ] <EOL> for limit in self . levels [ username ] : <EOL> delay = limit ( verb , url ) <EOL> if delay : <EOL> delays . append ( ( delay , limit . error_message ) ) <EOL> if delays : <EOL> delays . sort ( ) <EOL> return delays [ <NUM_LIT:0> ] <EOL> return None , None <EOL> @ staticmethod <EOL> def parse_limits ( limits ) : <EOL> """<STR_LIT>""" <EOL> limits = limits . strip ( ) <EOL> if not limits : <EOL> return [ ] <EOL> result = [ ] <EOL> for group in limits . split ( '<STR_LIT:;>' ) : <EOL> group = group . strip ( ) <EOL> if group [ : <NUM_LIT:1> ] != '<STR_LIT:(>' or group [ - <NUM_LIT:1> : ] != '<STR_LIT:)>' : <EOL> raise ValueError ( "<STR_LIT>" <EOL> "<STR_LIT>" ) <EOL> group = group [ <NUM_LIT:1> : - <NUM_LIT:1> ] <EOL> args = [ a . strip ( ) for a in group . split ( '<STR_LIT:U+002C>' ) ] <EOL> if len ( args ) != <NUM_LIT:5> : <EOL> raise ValueError ( "<STR_LIT>" <EOL> "<STR_LIT>" ) <EOL> verb , uri , regex , value , unit = args <EOL> verb = verb . upper ( ) <EOL> value = int ( value ) <EOL> unit = unit . upper ( ) <EOL> if unit not in Limit . UNIT_MAP : <EOL> raise ValueError ( "<STR_LIT>" ) <EOL> unit = Limit . UNIT_MAP [ unit ] <EOL> result . append ( Limit ( verb , uri , regex , value , unit ) ) <EOL> return result <EOL> class WsgiLimiter ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , limits = None ) : <EOL> """<STR_LIT>""" <EOL> self . _limiter = Limiter ( limits or DEFAULT_LIMITS ) <EOL> @ wsgify ( RequestClass = wsgi . Request ) <EOL> def __call__ ( self , request ) : <EOL> """<STR_LIT>""" <EOL> if request . method != "<STR_LIT:POST>" : <EOL> raise webob . exc . HTTPMethodNotAllowed ( ) <EOL> try : <EOL> info = dict ( json . loads ( request . body ) ) <EOL> except ValueError : <EOL> raise webob . exc . HTTPBadRequest ( ) <EOL> username = request . path_info_pop ( ) <EOL> verb = info . get ( "<STR_LIT>" ) <EOL> path = info . get ( "<STR_LIT:path>" ) <EOL> delay , error = self . _limiter . check_for_delay ( verb , path , username ) <EOL> if delay : <EOL> headers = { "<STR_LIT>" : "<STR_LIT>" % delay } <EOL> return webob . exc . HTTPForbidden ( headers = headers , explanation = error ) <EOL> else : <EOL> return webob . exc . HTTPNoContent ( ) <EOL> class WsgiLimiterProxy ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , limiter_address ) : <EOL> """<STR_LIT>""" <EOL> self . limiter_address = limiter_address <EOL> def check_for_delay ( self , verb , path , username = None ) : <EOL> body = json . dumps ( { "<STR_LIT>" : verb , "<STR_LIT:path>" : path } ) <EOL> headers = { "<STR_LIT:Content-Type>" : "<STR_LIT:application/json>" } <EOL> conn = httplib . HTTPConnection ( self . limiter_address ) <EOL> if username : <EOL> conn . request ( "<STR_LIT:POST>" , "<STR_LIT>" % ( username ) , body , headers ) <EOL> else : <EOL> conn . request ( "<STR_LIT:POST>" , "<STR_LIT:/>" , body , headers ) <EOL> resp = conn . getresponse ( ) <EOL> if <NUM_LIT:200> >= resp . status < <NUM_LIT> : <EOL> return None , None <EOL> return resp . getheader ( "<STR_LIT>" ) , resp . read ( ) or None <EOL> @ staticmethod <EOL> def parse_limits ( limits ) : <EOL> """<STR_LIT>""" <EOL> return [ ] </s>
