blob: 039ca4ad5d37b134cfb24d292fe1377696de0ac1 [file] [log] [blame]
adminbae64d82013-08-01 10:50:15 -07001#!/usr/bin/env python
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002'''
adminbae64d82013-08-01 10:50:15 -07003Created on 23-Oct-2012
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00004Copyright 2012 Open Networking Foundation (ONF)
Jon Hall4ba53f02015-07-29 13:07:41 -07005
Jeremy Songsterae01bba2016-07-11 15:39:17 -07006Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
7the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
8or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
adminbae64d82013-08-01 10:50:15 -07009
10 TestON is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 2 of the License, or
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000013 (at your option) any later version.
adminbae64d82013-08-01 10:50:15 -070014
15 TestON is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
Jon Hall4ba53f02015-07-29 13:07:41 -070021 along with TestON. If not, see <http://www.gnu.org/licenses/>.
adminbae64d82013-08-01 10:50:15 -070022
Jon Hall4ba53f02015-07-29 13:07:41 -070023
adminbae64d82013-08-01 10:50:15 -070024Utilities will take care about the basic functions like :
25 * Extended assertion,
26 * parse_args for key-value pair handling
27 * Parsing the params or topology file.
28
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000029'''
adminbae64d82013-08-01 10:50:15 -070030import re
31from configobj import ConfigObj
adminbae64d82013-08-01 10:50:15 -070032from core import ast as ast
33import smtplib
34
adminbae64d82013-08-01 10:50:15 -070035import email
36import os
37import email.mime.application
Jon Hall095730a2015-12-17 14:57:45 -080038import time
39import random
adminbae64d82013-08-01 10:50:15 -070040
Jon Hall4dbbbd62021-10-18 15:33:42 -070041
adminbae64d82013-08-01 10:50:15 -070042class Utilities:
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000043 '''
adminbae64d82013-08-01 10:50:15 -070044 Utilities will take care about the basic functions like :
45 * Extended assertion,
46 * parse_args for key-value pair handling
47 * Parsing the params or topology file.
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000048 '''
Jon Hall4ba53f02015-07-29 13:07:41 -070049
Jeremy Ronquillo696f4262017-10-17 10:56:26 -070050 def __init__( self ):
51 self.wrapped = sys.modules[ __name__ ]
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000052
Jeremy Ronquillo696f4262017-10-17 10:56:26 -070053 def __getattr__( self, name ):
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000054 '''
adminbae64d82013-08-01 10:50:15 -070055 This will invoke, if the attribute wasn't found the usual ways.
56 Here it will look for assert_attribute and will execute when AttributeError occurs.
57 It will return the result of the assert_attribute.
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000058 '''
adminbae64d82013-08-01 10:50:15 -070059 try:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -070060 return getattr( self.wrapped, name )
adminbae64d82013-08-01 10:50:15 -070061 except AttributeError:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -070062 def assertHandling( **kwargs ):
63 nameVar = re.match( "^assert", name, flags=0 )
64 matchVar = re.match( "assert(_not_|_)(equals|matches|greater|lesser)", name, flags=0 )
adminbae64d82013-08-01 10:50:15 -070065 notVar = 0
66 operators = ""
67
Jeremy Ronquillo696f4262017-10-17 10:56:26 -070068 try:
69 if matchVar.group( 1 ) == "_not_" and matchVar.group( 2 ):
adminbae64d82013-08-01 10:50:15 -070070 notVar = 1
Jeremy Ronquillo696f4262017-10-17 10:56:26 -070071 operators = matchVar.group( 2 )
72 elif matchVar.group( 1 ) == "_" and matchVar.group( 2 ):
73 operators = matchVar.group( 2 )
adminbae64d82013-08-01 10:50:15 -070074 except AttributeError:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -070075 if matchVar is None and nameVar:
76 operators = 'equals'
77 result = self._assert( NOT=notVar, operator=operators, **kwargs )
adminbae64d82013-08-01 10:50:15 -070078 if result == main.TRUE:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -070079 main.log.info( "Assertion Passed" )
Jon Hall79bec222015-04-30 16:23:30 -070080 main.STEPRESULT = main.TRUE
adminbae64d82013-08-01 10:50:15 -070081 elif result == main.FALSE:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -070082 main.log.warn( "Assertion Failed" )
Jon Hall79bec222015-04-30 16:23:30 -070083 main.STEPRESULT = main.FALSE
84 else:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -070085 main.log.error( "There is an Error in Assertion" )
Jon Hall79bec222015-04-30 16:23:30 -070086 main.STEPRESULT = main.ERROR
adminbae64d82013-08-01 10:50:15 -070087 return result
adminbae64d82013-08-01 10:50:15 -070088 return assertHandling
Jon Hall79bec222015-04-30 16:23:30 -070089
Jeremy Ronquillo696f4262017-10-17 10:56:26 -070090 def _assert( self, **assertParam ):
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000091 '''
adminbae64d82013-08-01 10:50:15 -070092 It will take the arguments :
Jon Hall8ce34e82015-06-05 10:41:45 -070093 expect:'Expected output'
94 actual:'Actual output'
adminbae64d82013-08-01 10:50:15 -070095 onpass:'Action or string to be triggered or displayed respectively when the assert passed'
96 onfail:'Action or string to be triggered or displayed respectively when the assert failed'
97 not:'optional argument to specify the negation of the each assertion type'
98 operator:'assertion type will be defined by using operator. Like equal , greater, lesser, matches.'
Jon Hall4dbbbd62021-10-18 15:33:42 -070099 onfailFunc: Function to run when the assert fails. Will default to a noop function.
100 onfailFuncArgs: Arguments for onfailFunc
101 onfailFuncKwargs: Keyword Arguments for onfailFunc
Jon Hall8ce34e82015-06-05 10:41:45 -0700102
adminbae64d82013-08-01 10:50:15 -0700103 It will return the assertion result.
Jon Hall8ce34e82015-06-05 10:41:45 -0700104
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000105 '''
Jon Hall4dbbbd62021-10-18 15:33:42 -0700106 def noop( *args, **kwargs ):
107 pass
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000108
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700109 arguments = self.parse_args( [ "EXPECT", "ACTUAL", "ONPASS", "ONFAIL", "NOT", "OPERATOR" ], **assertParam )
Jon Hall4dbbbd62021-10-18 15:33:42 -0700110 onfailFunc = noop
111 onfailFunc = assertParam.get( "onfailFunc", noop )
112 onfailFuncArgs = assertParam.get( "onfailFuncArgs", [] )
113 onfailFuncKwargs = assertParam.get( "onfailFuncKwargs", {} )
Jon Hall8ce34e82015-06-05 10:41:45 -0700114
adminbae64d82013-08-01 10:50:15 -0700115 result = 0
116 valuetype = ''
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700117 operation = "not " + str( arguments[ "OPERATOR" ] ) if arguments[ 'NOT' ] and arguments[ 'NOT' ] == 1 else arguments[ "OPERATOR" ]
Jon Hall4dbbbd62021-10-18 15:33:42 -0700118 operators = { 'equals': { 'STR': '==', 'NUM': '==' }, 'matches': '=~', 'greater': '>', 'lesser': '<' }
Jon Hall8ce34e82015-06-05 10:41:45 -0700119
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700120 expectMatch = re.match( '^\s*[+-]?0(e0)?\s*$', str( arguments[ "EXPECT" ] ), re.I + re.M )
121 if not( ( not expectMatch ) and ( arguments[ "EXPECT" ] == 0 ) ):
adminbae64d82013-08-01 10:50:15 -0700122 valuetype = 'NUM'
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700123 else:
124 if arguments[ "OPERATOR" ] == 'greater' or arguments[ "OPERATOR" ] == 'lesser':
125 main.log.error( "Numeric comparison on strings is not possibele" )
adminbae64d82013-08-01 10:50:15 -0700126 return main.ERROR
Jon Hall8ce34e82015-06-05 10:41:45 -0700127
adminbae64d82013-08-01 10:50:15 -0700128 valuetype = 'STR'
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700129 arguments[ "ACTUAL" ] = str( arguments[ "ACTUAL" ] )
130 if arguments[ "OPERATOR" ] != 'matches':
131 arguments[ "EXPECT" ] = str( arguments[ "EXPECT" ] )
Jon Hall8ce34e82015-06-05 10:41:45 -0700132
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700133 try:
134 opcode = operators[ str( arguments[ "OPERATOR" ] ) ][ valuetype ] if arguments[ "OPERATOR" ] == 'equals' else operators[ str( arguments[ "OPERATOR" ] ) ]
Jon Hall8ce34e82015-06-05 10:41:45 -0700135
Jon Hall4dbbbd62021-10-18 15:33:42 -0700136 except KeyError:
137 main.log.exeception( "Key Error in assertion" )
adminbae64d82013-08-01 10:50:15 -0700138 return main.FALSE
Jon Hall8ce34e82015-06-05 10:41:45 -0700139
adminbae64d82013-08-01 10:50:15 -0700140 if opcode == '=~':
141 try:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700142 assert re.search( str( arguments[ "EXPECT" ] ), str( arguments[ "ACTUAL" ] ) )
adminbae64d82013-08-01 10:50:15 -0700143 result = main.TRUE
144 except AssertionError:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700145 try:
146 assert re.match( str( arguments[ "EXPECT" ] ), str( arguments[ "ACTUAL" ] ) )
adminbae64d82013-08-01 10:50:15 -0700147 result = main.TRUE
148 except AssertionError:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700149 main.log.error( "Assertion Failed" )
adminbae64d82013-08-01 10:50:15 -0700150 result = main.FALSE
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700151 else:
adminbae64d82013-08-01 10:50:15 -0700152 try:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700153 if str( opcode ) == "==":
154 main.log.info( "Verifying the Expected is equal to the actual or not using assert_equal" )
Jon Hall4dbbbd62021-10-18 15:33:42 -0700155 if arguments[ "EXPECT" ] == arguments[ "ACTUAL" ]:
adminbae64d82013-08-01 10:50:15 -0700156 result = main.TRUE
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700157 else:
adminbae64d82013-08-01 10:50:15 -0700158 result = main.FALSE
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700159 elif str( opcode ) == ">":
160 main.log.info( "Verifying the Expected is Greater than the actual or not using assert_greater" )
Jon Hall4dbbbd62021-10-18 15:33:42 -0700161 if ast.literal_eval( arguments[ "EXPECT" ] ) > ast.literal_eval( arguments[ "ACTUAL" ] ):
adminbae64d82013-08-01 10:50:15 -0700162 result = main.TRUE
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700163 else:
adminbae64d82013-08-01 10:50:15 -0700164 result = main.FALSE
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700165 elif str( opcode ) == "<":
166 main.log.info( "Verifying the Expected is Lesser than the actual or not using assert_lesser" )
Jon Hall4dbbbd62021-10-18 15:33:42 -0700167 if ast.literal_eval( arguments[ "EXPECT" ] ) < ast.literal_eval( arguments[ "ACTUAL" ] ):
adminbae64d82013-08-01 10:50:15 -0700168 result = main.TRUE
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700169 else:
adminbae64d82013-08-01 10:50:15 -0700170 result = main.FALSE
adminbae64d82013-08-01 10:50:15 -0700171 except AssertionError:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700172 main.log.error( "Assertion Failed" )
adminbae64d82013-08-01 10:50:15 -0700173 result = main.FALSE
adminbae64d82013-08-01 10:50:15 -0700174 result = result if result else 0
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700175 result = not result if arguments[ "NOT" ] and arguments[ "NOT" ] == 1 else result
adminbae64d82013-08-01 10:50:15 -0700176 resultString = ""
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700177 if result:
178 resultString = str( resultString ) + "PASS"
179 main.log.info( arguments[ "ONPASS" ] )
180 else:
181 resultString = str( resultString ) + "FAIL"
182 if not isinstance( arguments[ "ONFAIL" ], str ):
183 eval( str( arguments[ "ONFAIL" ] ) )
184 else:
185 main.log.error( arguments[ "ONFAIL" ] )
186 main.log.report( arguments[ "ONFAIL" ] )
Jon Hall90627612015-06-09 14:57:02 -0700187 main.onFailMsg = arguments[ 'ONFAIL' ]
Jon Hall8ce34e82015-06-05 10:41:45 -0700188
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700189 msg = arguments[ "ON" + str( resultString ) ]
adminbae64d82013-08-01 10:50:15 -0700190
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700191 if not isinstance( msg, str ):
adminbae64d82013-08-01 10:50:15 -0700192 try:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700193 eval( str( msg ) )
Jon Hall4dbbbd62021-10-18 15:33:42 -0700194 except SyntaxError:
195 main.log.exception( "function definition is not right" )
adminbae64d82013-08-01 10:50:15 -0700196
197 main.last_result = result
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700198 if main.stepResults[ 2 ]:
199 main.stepResults[ 2 ][ -1 ] = result
Jon Hall80577e72015-09-29 14:07:25 -0700200 try:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700201 main.stepResults[ 3 ][ -1 ] = arguments[ 'ONFAIL' ]
Jon Hall80577e72015-09-29 14:07:25 -0700202 except AttributeError:
203 pass
204 else:
205 main.log.warn( "Assertion called before a test step" )
Jon Hall4dbbbd62021-10-18 15:33:42 -0700206 try:
207 if result != main.TRUE:
208 onfailFunc( *onfailFuncArgs, **onfailFuncKwargs )
209 except SkipCase:
210 raise
211 except Exception:
212 main.log.exception( "Error calling onfailFunc: %s" % onfailFunc )
213 return False
214 else:
215 return result
Jon Hall8ce34e82015-06-05 10:41:45 -0700216
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700217 def parse_args( self, args, **kwargs ):
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000218 '''
219 It will accept the (key,value) pair and will return the (key,value) pairs with keys in uppercase.
220 '''
adminbae64d82013-08-01 10:50:15 -0700221 newArgs = {}
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700222 for key, value in kwargs.iteritems():
223 if isinstance( args, list ) and str.upper( key ) in args:
Jon Hall4ba53f02015-07-29 13:07:41 -0700224 for each in args:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700225 if each == str.upper( key ):
226 newArgs[ str( each ) ] = value
227 elif each != str.upper( key ) and str( each ) not in newArgs:
228 newArgs[ str( each ) ] = None
Jon Hall4ba53f02015-07-29 13:07:41 -0700229
adminbae64d82013-08-01 10:50:15 -0700230 return newArgs
Jon Hall4ba53f02015-07-29 13:07:41 -0700231
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700232 def send_mail( self ):
adminbae64d82013-08-01 10:50:15 -0700233 # Create a text/plain message
234 msg = email.mime.Multipart.MIMEMultipart()
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700235 try:
adminbae64d82013-08-01 10:50:15 -0700236 if main.test_target:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700237 sub = "Result summary of \"" + main.TEST + "\" run on component \"" + \
238 main.test_target + "\" Version \"" + \
239 vars( main )[ main.test_target ].get_version() + "\": " + \
You Wangc2e22b22019-02-28 17:11:11 -0800240 str( main.TOTAL_TC_SUCCESS_PERCENT ) + "% Passed"
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700241 else:
242 sub = "Result summary of \"" + main.TEST + "\": " + \
You Wangc2e22b22019-02-28 17:11:11 -0800243 str( main.TOTAL_TC_SUCCESS_PERCENT ) + "% Passed"
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700244 except( KeyError, AttributeError ):
245 sub = "Result summary of \"" + main.TEST + "\": " + \
You Wangc2e22b22019-02-28 17:11:11 -0800246 str( main.TOTAL_TC_SUCCESS_PERCENT ) + "% Passed"
Jon Hall4ba53f02015-07-29 13:07:41 -0700247
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700248 msg[ 'Subject' ] = sub
249 msg[ 'From' ] = main.sender
250 msg[ 'To' ] = main.mail
Jon Hall4ba53f02015-07-29 13:07:41 -0700251
adminbae64d82013-08-01 10:50:15 -0700252 # The main body is just another attachment
Jon Hall25079782015-10-13 13:54:39 -0700253 body = email.mime.Text.MIMEText( main.logHeader + "\n" +
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700254 main.testResult )
Jon Hall25079782015-10-13 13:54:39 -0700255 msg.attach( body )
Jon Hall4ba53f02015-07-29 13:07:41 -0700256
Jon Hall25079782015-10-13 13:54:39 -0700257 # Attachments
258 for filename in os.listdir( main.logdir ):
259 filepath = main.logdir + "/" + filename
260 fp = open( filepath, 'rb' )
261 att = email.mime.application.MIMEApplication( fp.read(),
262 _subtype="" )
adminbae64d82013-08-01 10:50:15 -0700263 fp.close()
Jon Hall25079782015-10-13 13:54:39 -0700264 att.add_header( 'Content-Disposition',
265 'attachment',
266 filename=filename )
267 msg.attach( att )
268 try:
269 smtp = smtplib.SMTP( main.smtp )
270 smtp.starttls()
271 smtp.login( main.sender, main.senderPwd )
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700272 smtp.sendmail( msg[ 'From' ], [ msg[ 'To' ]], msg.as_string() )
Jon Hall25079782015-10-13 13:54:39 -0700273 smtp.quit()
274 except Exception:
275 main.log.exception( "Error sending email" )
Jon Hall4ba53f02015-07-29 13:07:41 -0700276 return main.TRUE
277
Jon Hall25079782015-10-13 13:54:39 -0700278 def send_warning_email( self, subject=None ):
279 try:
280 if not subject:
281 subject = main.TEST + " PAUSED!"
282 # Create a text/plain message
283 msg = email.mime.Multipart.MIMEMultipart()
284
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700285 msg[ 'Subject' ] = subject
286 msg[ 'From' ] = main.sender
287 msg[ 'To' ] = main.mail
Jon Hall25079782015-10-13 13:54:39 -0700288
289 smtp = smtplib.SMTP( main.smtp )
290 smtp.starttls()
291 smtp.login( main.sender, main.senderPwd )
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700292 smtp.sendmail( msg[ 'From' ], [ msg[ 'To' ]], msg.as_string() )
Jon Hall25079782015-10-13 13:54:39 -0700293 smtp.quit()
294 except Exception:
295 main.log.exception( "" )
296 return main.FALSE
297 return main.TRUE
Jon Hall4ba53f02015-07-29 13:07:41 -0700298
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700299 def parse( self, fileName ):
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000300 '''
adminbae64d82013-08-01 10:50:15 -0700301 This will parse the params or topo or cfg file and return content in the file as Dictionary
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000302 '''
adminbae64d82013-08-01 10:50:15 -0700303 self.fileName = fileName
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700304 matchFileName = re.match( r'(.*)\.(cfg|params|topo)', self.fileName, re.M | re.I )
adminbae64d82013-08-01 10:50:15 -0700305 if matchFileName:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700306 try:
307 parsedInfo = ConfigObj( self.fileName )
adminbae64d82013-08-01 10:50:15 -0700308 return parsedInfo
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000309 except StandardError:
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700310 print "There is no such file to parse " + fileName
adminbae64d82013-08-01 10:50:15 -0700311 else:
Jon Hall4ba53f02015-07-29 13:07:41 -0700312 return 0
adminbae64d82013-08-01 10:50:15 -0700313
Jon Hall095730a2015-12-17 14:57:45 -0800314 def retry( self, f, retValue, args=(), kwargs={},
Devin Lima4f95bc2017-08-11 11:13:03 -0700315 sleep=1, attempts=2, randomTime=False,
316 getRetryingTime=False ):
Jon Hall095730a2015-12-17 14:57:45 -0800317 """
318 Given a function and bad return values, retry will retry a function
319 until successful or give up after a certain number of attempts.
320
321 Arguments:
322 f - a callable object
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000323 retValue - Return value(s) of f to retry on. This can be a list or an
Jon Hall095730a2015-12-17 14:57:45 -0800324 object.
325 args - A tuple containing the arguments of f.
326 kwargs - A dictionary containing the keyword arguments of f.
327 sleep - Time in seconds to sleep between retries. If random is True,
328 this is the max time to wait. Defaults to 1 second.
329 attempts - Max number of attempts before returning. If set to 1,
330 f will only be called once. Defaults to 2 trys.
331 random - Boolean indicating if the wait time is random between 0
332 and sleep or exactly sleep seconds. Defaults to False.
333 """
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000334 # TODO: be able to pass in a conditional statement(s). For example:
Jon Hall095730a2015-12-17 14:57:45 -0800335 # retCondition = "< 7"
336 # Then we do something like 'if eval( "ret " + retCondition ):break'
337 try:
338 assert attempts > 0, "attempts must be more than 1"
339 assert sleep >= 0, "sleep must be >= 0"
340 if not isinstance( retValue, list ):
341 retValue = [ retValue ]
Devin Lima4f95bc2017-08-11 11:13:03 -0700342 if getRetryingTime:
343 startTime = time.time()
Jon Hall095730a2015-12-17 14:57:45 -0800344 for i in range( 0, attempts ):
345 ret = f( *args, **kwargs )
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700346 if ret not in retValue: # NOTE that False in [ 0 ] == True
Jon Hall095730a2015-12-17 14:57:45 -0800347 break
348 if randomTime:
349 sleeptime = random.randint( 0, sleep )
350 else:
351 sleeptime = sleep
352 time.sleep( sleeptime )
Jon Hall2ad817e2017-05-18 11:13:10 -0700353 if i > 0:
354 main.log.debug( str( f ) + " was retried " + str( i ) + " times." )
Devin Lima4f95bc2017-08-11 11:13:03 -0700355 if getRetryingTime:
356 main.log.debug( "Took " + str( time.time() - startTime ) + " seconds for retrying." )
Jon Hall095730a2015-12-17 14:57:45 -0800357 return ret
358 except AssertionError:
359 main.log.exception( "Invalid arguements for retry: " )
Devin Lim44075962017-08-11 10:56:37 -0700360 main.cleanAndExit()
Jon Hall095730a2015-12-17 14:57:45 -0800361 except Exception:
362 main.log.exception( "Uncaught exception in retry: " )
Devin Lim44075962017-08-11 10:56:37 -0700363 main.cleanAndExit()
Jon Hall095730a2015-12-17 14:57:45 -0800364
adminbae64d82013-08-01 10:50:15 -0700365
366if __name__ != "__main__":
367 import sys
Jon Hall4ba53f02015-07-29 13:07:41 -0700368
Jeremy Ronquillo696f4262017-10-17 10:56:26 -0700369 sys.modules[ __name__ ] = Utilities()