admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python |
Jeremy Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 2 | ''' |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 3 | Created on 23-Oct-2012 |
Jeremy Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 4 | Copyright 2012 Open Networking Foundation (ONF) |
Jon Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 5 | |
Jeremy Songster | ae01bba | 2016-07-11 15:39:17 -0700 | [diff] [blame] | 6 | Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>, |
| 7 | the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>, |
| 8 | or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg> |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 9 | |
| 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 Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 13 | (at your option) any later version. |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 14 | |
| 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 Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 21 | along with TestON. If not, see <http://www.gnu.org/licenses/>. |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 22 | |
Jon Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 23 | |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 24 | Utilities 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 Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 29 | ''' |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 30 | import re |
| 31 | from configobj import ConfigObj |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 32 | from core import ast as ast |
| 33 | import smtplib |
| 34 | |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 35 | import email |
| 36 | import os |
| 37 | import email.mime.application |
Jon Hall | 095730a | 2015-12-17 14:57:45 -0800 | [diff] [blame] | 38 | import time |
| 39 | import random |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 40 | |
Jon Hall | 4dbbbd6 | 2021-10-18 15:33:42 -0700 | [diff] [blame] | 41 | |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 42 | class Utilities: |
Jeremy Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 43 | ''' |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 44 | 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 Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 48 | ''' |
Jon Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 49 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 50 | def __init__( self ): |
| 51 | self.wrapped = sys.modules[ __name__ ] |
Jeremy Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 52 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 53 | def __getattr__( self, name ): |
Jeremy Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 54 | ''' |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 55 | 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 Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 58 | ''' |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 59 | try: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 60 | return getattr( self.wrapped, name ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 61 | except AttributeError: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 62 | def assertHandling( **kwargs ): |
| 63 | nameVar = re.match( "^assert", name, flags=0 ) |
| 64 | matchVar = re.match( "assert(_not_|_)(equals|matches|greater|lesser)", name, flags=0 ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 65 | notVar = 0 |
| 66 | operators = "" |
| 67 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 68 | try: |
| 69 | if matchVar.group( 1 ) == "_not_" and matchVar.group( 2 ): |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 70 | notVar = 1 |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 71 | operators = matchVar.group( 2 ) |
| 72 | elif matchVar.group( 1 ) == "_" and matchVar.group( 2 ): |
| 73 | operators = matchVar.group( 2 ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 74 | except AttributeError: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 75 | if matchVar is None and nameVar: |
| 76 | operators = 'equals' |
| 77 | result = self._assert( NOT=notVar, operator=operators, **kwargs ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 78 | if result == main.TRUE: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 79 | main.log.info( "Assertion Passed" ) |
Jon Hall | 79bec22 | 2015-04-30 16:23:30 -0700 | [diff] [blame] | 80 | main.STEPRESULT = main.TRUE |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 81 | elif result == main.FALSE: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 82 | main.log.warn( "Assertion Failed" ) |
Jon Hall | 79bec22 | 2015-04-30 16:23:30 -0700 | [diff] [blame] | 83 | main.STEPRESULT = main.FALSE |
| 84 | else: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 85 | main.log.error( "There is an Error in Assertion" ) |
Jon Hall | 79bec22 | 2015-04-30 16:23:30 -0700 | [diff] [blame] | 86 | main.STEPRESULT = main.ERROR |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 87 | return result |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 88 | return assertHandling |
Jon Hall | 79bec22 | 2015-04-30 16:23:30 -0700 | [diff] [blame] | 89 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 90 | def _assert( self, **assertParam ): |
Jeremy Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 91 | ''' |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 92 | It will take the arguments : |
Jon Hall | 8ce34e8 | 2015-06-05 10:41:45 -0700 | [diff] [blame] | 93 | expect:'Expected output' |
| 94 | actual:'Actual output' |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 95 | 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 Hall | 4dbbbd6 | 2021-10-18 15:33:42 -0700 | [diff] [blame] | 99 | 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 Hall | 8ce34e8 | 2015-06-05 10:41:45 -0700 | [diff] [blame] | 102 | |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 103 | It will return the assertion result. |
Jon Hall | 8ce34e8 | 2015-06-05 10:41:45 -0700 | [diff] [blame] | 104 | |
Jeremy Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 105 | ''' |
Jon Hall | 4dbbbd6 | 2021-10-18 15:33:42 -0700 | [diff] [blame] | 106 | def noop( *args, **kwargs ): |
| 107 | pass |
Jeremy Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 108 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 109 | arguments = self.parse_args( [ "EXPECT", "ACTUAL", "ONPASS", "ONFAIL", "NOT", "OPERATOR" ], **assertParam ) |
Jon Hall | 4dbbbd6 | 2021-10-18 15:33:42 -0700 | [diff] [blame] | 110 | onfailFunc = noop |
| 111 | onfailFunc = assertParam.get( "onfailFunc", noop ) |
| 112 | onfailFuncArgs = assertParam.get( "onfailFuncArgs", [] ) |
| 113 | onfailFuncKwargs = assertParam.get( "onfailFuncKwargs", {} ) |
Jon Hall | 8ce34e8 | 2015-06-05 10:41:45 -0700 | [diff] [blame] | 114 | |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 115 | result = 0 |
| 116 | valuetype = '' |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 117 | operation = "not " + str( arguments[ "OPERATOR" ] ) if arguments[ 'NOT' ] and arguments[ 'NOT' ] == 1 else arguments[ "OPERATOR" ] |
Jon Hall | 4dbbbd6 | 2021-10-18 15:33:42 -0700 | [diff] [blame] | 118 | operators = { 'equals': { 'STR': '==', 'NUM': '==' }, 'matches': '=~', 'greater': '>', 'lesser': '<' } |
Jon Hall | 8ce34e8 | 2015-06-05 10:41:45 -0700 | [diff] [blame] | 119 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 120 | expectMatch = re.match( '^\s*[+-]?0(e0)?\s*$', str( arguments[ "EXPECT" ] ), re.I + re.M ) |
| 121 | if not( ( not expectMatch ) and ( arguments[ "EXPECT" ] == 0 ) ): |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 122 | valuetype = 'NUM' |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 123 | else: |
| 124 | if arguments[ "OPERATOR" ] == 'greater' or arguments[ "OPERATOR" ] == 'lesser': |
| 125 | main.log.error( "Numeric comparison on strings is not possibele" ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 126 | return main.ERROR |
Jon Hall | 8ce34e8 | 2015-06-05 10:41:45 -0700 | [diff] [blame] | 127 | |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 128 | valuetype = 'STR' |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 129 | arguments[ "ACTUAL" ] = str( arguments[ "ACTUAL" ] ) |
| 130 | if arguments[ "OPERATOR" ] != 'matches': |
| 131 | arguments[ "EXPECT" ] = str( arguments[ "EXPECT" ] ) |
Jon Hall | 8ce34e8 | 2015-06-05 10:41:45 -0700 | [diff] [blame] | 132 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 133 | try: |
| 134 | opcode = operators[ str( arguments[ "OPERATOR" ] ) ][ valuetype ] if arguments[ "OPERATOR" ] == 'equals' else operators[ str( arguments[ "OPERATOR" ] ) ] |
Jon Hall | 8ce34e8 | 2015-06-05 10:41:45 -0700 | [diff] [blame] | 135 | |
Jon Hall | 4dbbbd6 | 2021-10-18 15:33:42 -0700 | [diff] [blame] | 136 | except KeyError: |
| 137 | main.log.exeception( "Key Error in assertion" ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 138 | return main.FALSE |
Jon Hall | 8ce34e8 | 2015-06-05 10:41:45 -0700 | [diff] [blame] | 139 | |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 140 | if opcode == '=~': |
| 141 | try: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 142 | assert re.search( str( arguments[ "EXPECT" ] ), str( arguments[ "ACTUAL" ] ) ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 143 | result = main.TRUE |
| 144 | except AssertionError: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 145 | try: |
| 146 | assert re.match( str( arguments[ "EXPECT" ] ), str( arguments[ "ACTUAL" ] ) ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 147 | result = main.TRUE |
| 148 | except AssertionError: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 149 | main.log.error( "Assertion Failed" ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 150 | result = main.FALSE |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 151 | else: |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 152 | try: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 153 | if str( opcode ) == "==": |
| 154 | main.log.info( "Verifying the Expected is equal to the actual or not using assert_equal" ) |
Jon Hall | 4dbbbd6 | 2021-10-18 15:33:42 -0700 | [diff] [blame] | 155 | if arguments[ "EXPECT" ] == arguments[ "ACTUAL" ]: |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 156 | result = main.TRUE |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 157 | else: |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 158 | result = main.FALSE |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 159 | elif str( opcode ) == ">": |
| 160 | main.log.info( "Verifying the Expected is Greater than the actual or not using assert_greater" ) |
Jon Hall | 4dbbbd6 | 2021-10-18 15:33:42 -0700 | [diff] [blame] | 161 | if ast.literal_eval( arguments[ "EXPECT" ] ) > ast.literal_eval( arguments[ "ACTUAL" ] ): |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 162 | result = main.TRUE |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 163 | else: |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 164 | result = main.FALSE |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 165 | elif str( opcode ) == "<": |
| 166 | main.log.info( "Verifying the Expected is Lesser than the actual or not using assert_lesser" ) |
Jon Hall | 4dbbbd6 | 2021-10-18 15:33:42 -0700 | [diff] [blame] | 167 | if ast.literal_eval( arguments[ "EXPECT" ] ) < ast.literal_eval( arguments[ "ACTUAL" ] ): |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 168 | result = main.TRUE |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 169 | else: |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 170 | result = main.FALSE |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 171 | except AssertionError: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 172 | main.log.error( "Assertion Failed" ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 173 | result = main.FALSE |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 174 | result = result if result else 0 |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 175 | result = not result if arguments[ "NOT" ] and arguments[ "NOT" ] == 1 else result |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 176 | resultString = "" |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 177 | 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 Hall | 9062761 | 2015-06-09 14:57:02 -0700 | [diff] [blame] | 187 | main.onFailMsg = arguments[ 'ONFAIL' ] |
Jon Hall | 8ce34e8 | 2015-06-05 10:41:45 -0700 | [diff] [blame] | 188 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 189 | msg = arguments[ "ON" + str( resultString ) ] |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 190 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 191 | if not isinstance( msg, str ): |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 192 | try: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 193 | eval( str( msg ) ) |
Jon Hall | 4dbbbd6 | 2021-10-18 15:33:42 -0700 | [diff] [blame] | 194 | except SyntaxError: |
| 195 | main.log.exception( "function definition is not right" ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 196 | |
| 197 | main.last_result = result |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 198 | if main.stepResults[ 2 ]: |
| 199 | main.stepResults[ 2 ][ -1 ] = result |
Jon Hall | 80577e7 | 2015-09-29 14:07:25 -0700 | [diff] [blame] | 200 | try: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 201 | main.stepResults[ 3 ][ -1 ] = arguments[ 'ONFAIL' ] |
Jon Hall | 80577e7 | 2015-09-29 14:07:25 -0700 | [diff] [blame] | 202 | except AttributeError: |
| 203 | pass |
| 204 | else: |
| 205 | main.log.warn( "Assertion called before a test step" ) |
Jon Hall | 4dbbbd6 | 2021-10-18 15:33:42 -0700 | [diff] [blame] | 206 | 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 Hall | 8ce34e8 | 2015-06-05 10:41:45 -0700 | [diff] [blame] | 216 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 217 | def parse_args( self, args, **kwargs ): |
Jeremy Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 218 | ''' |
| 219 | It will accept the (key,value) pair and will return the (key,value) pairs with keys in uppercase. |
| 220 | ''' |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 221 | newArgs = {} |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 222 | for key, value in kwargs.iteritems(): |
| 223 | if isinstance( args, list ) and str.upper( key ) in args: |
Jon Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 224 | for each in args: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 225 | 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 Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 229 | |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 230 | return newArgs |
Jon Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 231 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 232 | def send_mail( self ): |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 233 | # Create a text/plain message |
| 234 | msg = email.mime.Multipart.MIMEMultipart() |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 235 | try: |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 236 | if main.test_target: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 237 | sub = "Result summary of \"" + main.TEST + "\" run on component \"" + \ |
| 238 | main.test_target + "\" Version \"" + \ |
| 239 | vars( main )[ main.test_target ].get_version() + "\": " + \ |
You Wang | c2e22b2 | 2019-02-28 17:11:11 -0800 | [diff] [blame] | 240 | str( main.TOTAL_TC_SUCCESS_PERCENT ) + "% Passed" |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 241 | else: |
| 242 | sub = "Result summary of \"" + main.TEST + "\": " + \ |
You Wang | c2e22b2 | 2019-02-28 17:11:11 -0800 | [diff] [blame] | 243 | str( main.TOTAL_TC_SUCCESS_PERCENT ) + "% Passed" |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 244 | except( KeyError, AttributeError ): |
| 245 | sub = "Result summary of \"" + main.TEST + "\": " + \ |
You Wang | c2e22b2 | 2019-02-28 17:11:11 -0800 | [diff] [blame] | 246 | str( main.TOTAL_TC_SUCCESS_PERCENT ) + "% Passed" |
Jon Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 247 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 248 | msg[ 'Subject' ] = sub |
| 249 | msg[ 'From' ] = main.sender |
| 250 | msg[ 'To' ] = main.mail |
Jon Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 251 | |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 252 | # The main body is just another attachment |
Jon Hall | 2507978 | 2015-10-13 13:54:39 -0700 | [diff] [blame] | 253 | body = email.mime.Text.MIMEText( main.logHeader + "\n" + |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 254 | main.testResult ) |
Jon Hall | 2507978 | 2015-10-13 13:54:39 -0700 | [diff] [blame] | 255 | msg.attach( body ) |
Jon Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 256 | |
Jon Hall | 2507978 | 2015-10-13 13:54:39 -0700 | [diff] [blame] | 257 | # 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="" ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 263 | fp.close() |
Jon Hall | 2507978 | 2015-10-13 13:54:39 -0700 | [diff] [blame] | 264 | 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 Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 272 | smtp.sendmail( msg[ 'From' ], [ msg[ 'To' ]], msg.as_string() ) |
Jon Hall | 2507978 | 2015-10-13 13:54:39 -0700 | [diff] [blame] | 273 | smtp.quit() |
| 274 | except Exception: |
| 275 | main.log.exception( "Error sending email" ) |
Jon Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 276 | return main.TRUE |
| 277 | |
Jon Hall | 2507978 | 2015-10-13 13:54:39 -0700 | [diff] [blame] | 278 | 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 Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 285 | msg[ 'Subject' ] = subject |
| 286 | msg[ 'From' ] = main.sender |
| 287 | msg[ 'To' ] = main.mail |
Jon Hall | 2507978 | 2015-10-13 13:54:39 -0700 | [diff] [blame] | 288 | |
| 289 | smtp = smtplib.SMTP( main.smtp ) |
| 290 | smtp.starttls() |
| 291 | smtp.login( main.sender, main.senderPwd ) |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 292 | smtp.sendmail( msg[ 'From' ], [ msg[ 'To' ]], msg.as_string() ) |
Jon Hall | 2507978 | 2015-10-13 13:54:39 -0700 | [diff] [blame] | 293 | smtp.quit() |
| 294 | except Exception: |
| 295 | main.log.exception( "" ) |
| 296 | return main.FALSE |
| 297 | return main.TRUE |
Jon Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 298 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 299 | def parse( self, fileName ): |
Jeremy Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 300 | ''' |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 301 | This will parse the params or topo or cfg file and return content in the file as Dictionary |
Jeremy Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 302 | ''' |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 303 | self.fileName = fileName |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 304 | matchFileName = re.match( r'(.*)\.(cfg|params|topo)', self.fileName, re.M | re.I ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 305 | if matchFileName: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 306 | try: |
| 307 | parsedInfo = ConfigObj( self.fileName ) |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 308 | return parsedInfo |
Jeremy Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 309 | except StandardError: |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 310 | print "There is no such file to parse " + fileName |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 311 | else: |
Jon Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 312 | return 0 |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 313 | |
Jon Hall | 095730a | 2015-12-17 14:57:45 -0800 | [diff] [blame] | 314 | def retry( self, f, retValue, args=(), kwargs={}, |
Devin Lim | a4f95bc | 2017-08-11 11:13:03 -0700 | [diff] [blame] | 315 | sleep=1, attempts=2, randomTime=False, |
| 316 | getRetryingTime=False ): |
Jon Hall | 095730a | 2015-12-17 14:57:45 -0800 | [diff] [blame] | 317 | """ |
| 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 Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 323 | retValue - Return value(s) of f to retry on. This can be a list or an |
Jon Hall | 095730a | 2015-12-17 14:57:45 -0800 | [diff] [blame] | 324 | 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 Ronquillo | 4d5f1d0 | 2017-10-13 20:23:57 +0000 | [diff] [blame] | 334 | # TODO: be able to pass in a conditional statement(s). For example: |
Jon Hall | 095730a | 2015-12-17 14:57:45 -0800 | [diff] [blame] | 335 | # 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 Lim | a4f95bc | 2017-08-11 11:13:03 -0700 | [diff] [blame] | 342 | if getRetryingTime: |
| 343 | startTime = time.time() |
Jon Hall | 095730a | 2015-12-17 14:57:45 -0800 | [diff] [blame] | 344 | for i in range( 0, attempts ): |
| 345 | ret = f( *args, **kwargs ) |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 346 | if ret not in retValue: # NOTE that False in [ 0 ] == True |
Jon Hall | 095730a | 2015-12-17 14:57:45 -0800 | [diff] [blame] | 347 | break |
| 348 | if randomTime: |
| 349 | sleeptime = random.randint( 0, sleep ) |
| 350 | else: |
| 351 | sleeptime = sleep |
| 352 | time.sleep( sleeptime ) |
Jon Hall | 2ad817e | 2017-05-18 11:13:10 -0700 | [diff] [blame] | 353 | if i > 0: |
| 354 | main.log.debug( str( f ) + " was retried " + str( i ) + " times." ) |
Devin Lim | a4f95bc | 2017-08-11 11:13:03 -0700 | [diff] [blame] | 355 | if getRetryingTime: |
| 356 | main.log.debug( "Took " + str( time.time() - startTime ) + " seconds for retrying." ) |
Jon Hall | 095730a | 2015-12-17 14:57:45 -0800 | [diff] [blame] | 357 | return ret |
| 358 | except AssertionError: |
| 359 | main.log.exception( "Invalid arguements for retry: " ) |
Devin Lim | 4407596 | 2017-08-11 10:56:37 -0700 | [diff] [blame] | 360 | main.cleanAndExit() |
Jon Hall | 095730a | 2015-12-17 14:57:45 -0800 | [diff] [blame] | 361 | except Exception: |
| 362 | main.log.exception( "Uncaught exception in retry: " ) |
Devin Lim | 4407596 | 2017-08-11 10:56:37 -0700 | [diff] [blame] | 363 | main.cleanAndExit() |
Jon Hall | 095730a | 2015-12-17 14:57:45 -0800 | [diff] [blame] | 364 | |
admin | bae64d8 | 2013-08-01 10:50:15 -0700 | [diff] [blame] | 365 | |
| 366 | if __name__ != "__main__": |
| 367 | import sys |
Jon Hall | 4ba53f0 | 2015-07-29 13:07:41 -0700 | [diff] [blame] | 368 | |
Jeremy Ronquillo | 696f426 | 2017-10-17 10:56:26 -0700 | [diff] [blame] | 369 | sys.modules[ __name__ ] = Utilities() |