blob: 33ed5d1d75892a3452ae20ef21ebae6af35fb761 [file] [log] [blame]
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -07001#!groovy
2// Copyright 2017 Open Networking Foundation (ONF)
3//
4// Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
5// the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
6// or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
7//
8// TestON is free software: you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by
10// the Free Software Foundation, either version 2 of the License, or
11// (at your option) any later version.
12//
13// TestON is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16// GNU General Public License for more details.
17//
18// You should have received a copy of the GNU General Public License
19// along with TestON. If not, see <http://www.gnu.org/licenses/>.
20
Jeremy Ronquillo336110a2019-07-11 14:20:40 -070021import groovy.time.TimeCategory
22import groovy.time.TimeDuration
23
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -070024// read the dependency files
Jeremy Ronquillo336110a2019-07-11 14:20:40 -070025graphs = evaluate readTrusted( 'TestON/JenkinsFile/dependencies/JenkinsGraphs.groovy' )
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -070026test_list = evaluate readTrusted( 'TestON/JenkinsFile/dependencies/JenkinsTestONTests.groovy' )
27fileRelated = evaluate readTrusted( 'TestON/JenkinsFile/dependencies/JenkinsPathAndFiles.groovy' )
28SCPFfuncs = evaluate readTrusted( 'TestON/JenkinsFile/dependencies/PerformanceFuncs.groovy' )
29
Jeremy Ronquillo6da78cf2019-07-29 11:47:19 -070030INITIALIZATION_TIMEOUT_MINUTES = 10 // timeout init() function if it takes too long.
31
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -070032category = null
33prop = null
34testsToRun = null
Jeremy Ronquilloa8490fb2019-06-26 11:59:50 -070035testsToRunStrList = null
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -070036branch = null
Jeremy Ronquillo336110a2019-07-11 14:20:40 -070037branchWithPrefix = null
Jeremy Ronquilloa8490fb2019-06-26 11:59:50 -070038start = null
Jeremy Ronquillo336110a2019-07-11 14:20:40 -070039nodeLabel = null
Jeremy Ronquillo6fbfdd52019-07-09 13:49:34 -070040testStation = null
Jeremy Ronquillo336110a2019-07-11 14:20:40 -070041testsOverride = null
42isGraphOnly = false
43isSCPF = false
Jeremy Ronquillo6da78cf2019-07-29 11:47:19 -070044pipelineTimeout = null
45
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -070046testsFromList = [:]
47graphPaths = [:]
48pipeline = [:]
49
50main()
51
52def main(){
Jeremy Ronquillo6da78cf2019-07-29 11:47:19 -070053 timeout( time: INITIALIZATION_TIMEOUT_MINUTES, unit: "MINUTES" ){
54 init()
55 }
56 timeout( time: pipelineTimeout, unit: "MINUTES" ){
57 runTests()
58 generateGraphs()
59 sendToSlack()
60 }
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -070061}
62
63def init(){
64 fileRelated.init()
65 test_list.init()
66 readParams()
67
68 if ( category == "SCPF" ){
Jeremy Ronquillo336110a2019-07-11 14:20:40 -070069 isSCPF = true
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -070070 SCPFfuncs.init()
Jeremy Ronquillo442ce4d2019-07-26 16:35:11 -070071 graphs.initialize( SCPFfuncs );
Jeremy Ronquillo336110a2019-07-11 14:20:40 -070072 prop = getProperties()
73 isOldFlow = ( prop[ "isOldFlow" ] == "true" )
Jeremy Ronquilloa8490fb2019-06-26 11:59:50 -070074 SCPFfuncs.oldFlowRuleCheck( isOldFlow, prop[ "ONOSBranch" ] )
Jeremy Ronquilloa8490fb2019-06-26 11:59:50 -070075 } else {
Jeremy Ronquillo336110a2019-07-11 14:20:40 -070076 isSCPF = false
77 graphs.initialize()
78 prop = getProperties()
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -070079 }
80
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -070081 // get the list of the test and init branch to it.
82 testsFromList = test_list.getTestsFromCategory( category )
83
84 initGraphPaths()
Jeremy Ronquillo336110a2019-07-11 14:20:40 -070085 tokenizeTokens = "\n;, "
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -070086
Jeremy Ronquillo336110a2019-07-11 14:20:40 -070087 if ( testsOverride == "" || testsOverride == null ){
88 testsToRunStrList = prop[ "Tests" ].tokenize( tokenizeTokens )
89 } else {
90 testsToRunStrList = testsOverride.tokenize( tokenizeTokens )
91 }
Jeremy Ronquilloa8490fb2019-06-26 11:59:50 -070092 testsToRun = test_list.getTestsFromStringList( testsToRunStrList )
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -070093}
94
95def readParams(){
Jeremy Ronquillo6fbfdd52019-07-09 13:49:34 -070096 category = params.Category // "FUNC", "HA", "USECASE", etc.
97 branch = params.Branch // "1.15", "2.1", "master", etc.
Jeremy Ronquillo336110a2019-07-11 14:20:40 -070098 branchWithPrefix = test_list.addPrefixToBranch( branch )
Jeremy Ronquillo6fbfdd52019-07-09 13:49:34 -070099 testStation = params.TestStation // "TestStation-BMs", etc.
100 nodeLabel = params.NodeLabel // "BM", "VM", "Fabric-1.x", etc.
Jeremy Ronquillo336110a2019-07-11 14:20:40 -0700101 testsOverride = params.TestsOverride // "FUNCflow, FUNCintent, [...]", overrides property file
102 isGraphOnly = params.OnlyRefreshGraphs // true or false
Jeremy Ronquillo6da78cf2019-07-29 11:47:19 -0700103 pipelineTimeout = params.TimeOut.toInteger() // integer minutes until the entire pipeline times out. Usually passed from upstream master-trigger job.
Jeremy Ronquillo336110a2019-07-11 14:20:40 -0700104}
105
106def getProperties(){
107 // get the properties of the test by reading the TestONOS.property
108
109 filePath = '''/var/jenkins/TestONOS-''' + category + '''-''' + branchWithPrefix + '''.property'''
110
111 node( testStation ) {
112 return readProperties( file: filePath )
113 }
114}
115
116def getCurrentTime(){
117 // get time of the PST zone.
118
119 TimeZone.setDefault( TimeZone.getTimeZone( 'PST' ) )
120 return new Date()
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -0700121}
122
123def initGraphPaths(){
Jeremy Ronquillo1e5d7f22019-07-17 14:18:42 -0700124 graphPaths.put( "trendIndividual", fileRelated.rScriptPaths[ "scripts" ][ "trendIndividual" ] )
Jeremy Ronquilloa8490fb2019-06-26 11:59:50 -0700125 if ( category == "SR" ){
Jeremy Ronquillo1e5d7f22019-07-17 14:18:42 -0700126 graphPaths.put( "saveDirectory", fileRelated.workspaces[ "base" ] + "postjob-" + ( testStation - "TestStation-" - "s" ) + "/" )
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -0700127 } else if ( category == "SRHA" ) {
Jeremy Ronquillo1e5d7f22019-07-17 14:18:42 -0700128 graphPaths.put( "saveDirectory", fileRelated.workspaces[ "Fabric" ] )
Jeremy Ronquillobb3001f2019-07-01 12:57:07 -0700129 } else if ( category == "SCPF" || category == "USECASE" ){
Jeremy Ronquillo1e5d7f22019-07-17 14:18:42 -0700130 graphPaths.put( "saveDirectory", fileRelated.workspaces[ "BM" ] )
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -0700131 } else {
Jeremy Ronquillo1e5d7f22019-07-17 14:18:42 -0700132 graphPaths.put( "saveDirectory", fileRelated.workspaces[ "VM" ] )
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -0700133 }
134}
135
136def runTests(){
137 // run the test sequentially and save the function into the dictionary.
138 for ( String test : testsToRun.keySet() ){
Jeremy Ronquillo336110a2019-07-11 14:20:40 -0700139 toBeRun = test
140 stepName = ( toBeRun ? "" : "Not " ) + "Running $test"
141 pureTestName = ( testsToRun[ test ].containsKey( "test" ) ? testsToRun[ test ][ "test" ].split().head() : test )
142 pipeline[ stepName ] = runTest( test,
143 toBeRun,
144 prop,
145 pureTestName,
146 isGraphOnly,
147 testsToRun,
148 graphPaths[ "trendIndividual" ],
149 graphPaths[ "saveDirectory" ] )
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -0700150 }
151
152 // get the start time of the test.
Jeremy Ronquillo336110a2019-07-11 14:20:40 -0700153 start = getCurrentTime()
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -0700154
155 // run the tests sequentially.
156 for ( test in pipeline.keySet() ){
157 pipeline[ test ].call()
158 }
159}
160
Jeremy Ronquillo336110a2019-07-11 14:20:40 -0700161def initTest(){
162 return '''#!/bin/bash -l
163 set -i # interactive
164 set +e
165 shopt -s expand_aliases # expand alias in non-interactive mode
166 export PYTHONUNBUFFERED=1
167 ifconfig
168 echo "ONOS Branch is: $ONOSBranch"
169 echo "TestON Branch is: $TestONBranch"
170 echo "Test date: "
171 date
172 cd ~
173 export PATH=$PATH:onos/tools/test/bin
174 timeout 240 stc shutdown | head -100
175 timeout 240 stc teardown | head -100
176 timeout 240 stc shutdown | head -100
177 cd ~/OnosSystemTest/TestON/bin
178 git log | head
179 ./cleanup.sh -f || true
180 '''
181}
182
183def runTestCli_py( testName, testCategory ){
184 // Bash script that will run the test.
185 // testName : name of the test
186 // testCategory : (SR,FUNC ... )
187
188 return '''cd ~/OnosSystemTest/TestON/bin
189 ./cli.py run ''' +
190 testName +
191 ''' --params GRAPH/nodeCluster=''' + nodeLabel
192}
193
194def concludeRunTest(){
195 return '''cd ~/OnosSystemTest/TestON/bin
196 ./cleanup.sh -f || true
197 # cleanup config changes
198 cd ~/onos/tools/package/config
199 git clean -df'''
200}
201
202def copyLogs(){
203 // bash script to copy the logs and other necessary element for SR tests.
204
205 result = ""
206 if ( category == "SR" ){
207 result = '''
208 sudo rm /var/jenkins/workspace/SR-log-${WikiPrefix}/*
209 sudo cp *karaf.log.* /var/jenkins/workspace/SR-log-${WikiPrefix}/
210 sudo cp *Flows* /var/jenkins/workspace/SR-log-${WikiPrefix}/
211 sudo cp *Groups* /var/jenkins/workspace/SR-log-${WikiPrefix}/
212 sudo cp *.tar.gz /var/jenkins/workspace/SR-log-${WikiPrefix}/
213 sudo cp t3-* /var/jenkins/workspace/SR-log-${WikiPrefix}/
214 '''
215 }
216 return result
217}
218
219def cleanAndCopyFiles( testName ){
220 // clean up some files that were in the folder and copy the new files from the log
221 // testName : name of the test
222
223 return '''#!/bin/bash -i
224 set +e
225 echo "ONOS Branch is: ${ONOSBranch}"
226 echo "TestON Branch is: ${TestONBranch}"
227 echo "Job name is: "''' + testName + '''
228 echo "Workspace is: ${WORKSPACE}/"
229 echo "Wiki page to post is: ${WikiPrefix}-"
230 # remove any leftover files from previous tests
231 sudo rm ${WORKSPACE}/*Wiki.txt
232 sudo rm ${WORKSPACE}/*Summary.txt
233 sudo rm ${WORKSPACE}/*Result.txt
234 sudo rm ${WORKSPACE}/*Alarm.txt || true
235 sudo rm ${WORKSPACE}/*.csv
236 #copy files to workspace
237 cd `ls -t ~/OnosSystemTest/TestON/logs/*/ | head -1 | sed 's/://'`
238 ''' + copyLogs() + '''
239 sudo cp *.txt ${WORKSPACE}/
240 sudo cp *.csv ${WORKSPACE}/
241 cd ${WORKSPACE}/
242 for i in *.csv
243 do mv "$i" "$WikiPrefix"-"$i"
244 done
245 ls -al
246 cd '''
247}
248
249def fetchLogs( testName ){
250 // fetch the logs of onos from onos nodes to onos System Test logs
251 // testName: name of the test
252
253 return '''#!/bin/bash
254 set +e
255 cd ~/OnosSystemTest/TestON/logs
256 echo "TestON test name is: "''' + testName + '''
257 TestONlogDir=$(ls -t | grep ${TEST_NAME}_ |head -1)
258 echo "########################################################################################"
259 echo "##### copying ONOS logs from all nodes to TestON/logs directory: ${TestONlogDir}"
260 echo "########################################################################################"
261 cd $TestONlogDir
262 if [ $? -eq 1 ]
263 then
264 echo "Job name does not match any test suite name to move log!"
265 else
266 pwd
267 for i in $OC{1..7}; do onos-fetch-logs $i || echo log does not exist for onos $i; done
268 for i in $OC{1..7}; do atomix-fetch-logs $i || echo log does not exist for atomix $i; done
269 fi
270 cd'''
271}
272
273def publishToConfluence( isManualRun, isPostResult, wikiLink, file ){
274 // publish HTML script to wiki confluence
275 // isManualRun : string "true" "false"
276 // isPostResult : string "true" "false"
277 // wikiLink : link of the wiki page to publish
278 // file : name of the file to be published
279
280 if ( isPostingResult( isManualRun, isPostResult ) ){
281 publishConfluence siteName: 'wiki.onosproject.org', pageName: wikiLink, spaceName: 'ONOS',
282 attachArchivedArtifacts: true, buildIfUnstable: true,
283 editorList: [ confluenceWritePage( confluenceFile( file ) ) ]
284 }
285}
286
287def postLogs( testName, prefix ){
288 // posting logs of the onos jobs specifically SR tests
289 // testName : name of the test
290 // prefix : branch prefix ( master, 2.1, 1.15 ... )
291
292 resultURL = ""
293 if ( category == "SR" ){
294 def post = build job: "SR-log-" + prefix, propagate: false
295 resultURL = post.getAbsoluteUrl()
296 }
297 return resultURL
298}
299
300def analyzeResult( prop, workSpace, pureTestName, testName, resultURL, wikiLink, isSCPF ){
301 // analyzing the result of the test and send to slack if any abnormal result is logged.
302 // prop : property dictionary
303 // workSpace : workSpace where the result file is saved
304 // pureTestName : TestON name of the test
305 // testName : Jenkins name of the test. Example: SCPFflowTPFobj
306 // resultURL : url for the logs for SR tests. Will not be posted if it is empty
307 // wikiLink : link of the wiki page where the result was posted
308 // isSCPF : Check if it is SCPF. If so, it won't post the wiki link.
309
310 node( testStation ) {
311 def alarmFile = workSpace + "/" + pureTestName + "Alarm.txt"
312 if ( fileExists( alarmFile ) ) {
313 def alarmContents = readFile( alarmFile )
314 slackSend( channel: "#jenkins-related",
315 color: "FF0000",
316 message: "[" + prop[ "ONOSBranch" ] + "]" + testName + " : triggered alarms:\n" +
317 alarmContents + "\n" +
318 "[TestON log] : \n" +
319 "https://jenkins.onosproject.org/blue/organizations/jenkins/${ env.JOB_NAME }/detail/${ env.JOB_NAME }/${ env.BUILD_NUMBER }/pipeline" +
320 ( isSCPF ? "" : ( "\n[Result on Wiki] : \n" +
321 "https://wiki.onosproject.org/display/ONOS/" +
322 wikiLink.replaceAll( "\\s", "+" ) ) ) +
323 ( resultURL != "" ? ( "\n[Karaf log] : \n" +
324 resultURL + "artifact/" ) : "" ),
325 teamDomain: 'onosproject' )
326 throw new Exception( "Abnormal test result." )
327 }
328 else {
329 print "Test results are OK."
330 }
331 }
332}
333
334def runTest( testName, toBeRun, prop, pureTestName, graphOnly, testCategory, graph_generator_file,
335 graph_saved_directory ){
336 // run the test on the machine that contains all the steps : init and run test, copy files, publish result ...
337 // testName : name of the test in Jenkins
338 // toBeRun : boolean value whether the test will be run or not. If not, it won't be run but shows up with empty
339 // result on pipeline view
340 // prop : dictionary property on the machine
341 // pureTestName : Pure name of the test. ( ex. pureTestName of SCPFflowTpFobj will be SCPFflowTp )
342 // graphOnly : check if it is generating graph job. If so, it will only generate the generating graph part
343 // testCategory : Map for the test suit ( SCPF, SR, FUNC, ... ) which contains information about the tests
344 // graph_generator_file : Rscript file with the full path.
345 // graph_saved_directory : where the generated graph will be saved to.
346
347 return {
348 catchError {
349 stage( testName ) {
350 if ( toBeRun ){
351 def workSpace = "/var/jenkins/workspace/" + testName
352 def fileContents = ""
353 node( testStation ) {
354 withEnv( [ 'ONOSBranch=' + prop[ "ONOSBranch" ],
355 'ONOSJAVAOPTS=' + prop[ "ONOSJAVAOPTS" ],
356 'TestONBranch=' + prop[ "TestONBranch" ],
357 'ONOSTag=' + prop[ "ONOSTag" ],
358 'WikiPrefix=' + prop[ "WikiPrefix" ],
359 'WORKSPACE=' + workSpace ] ) {
360 if ( !graphOnly ){
361 if ( isSCPF ){
362 // Remove the old database file
363 sh SCPFfuncs.cleanupDatabaseFile( testName )
364 }
365 sh script: initTest(), label: "Test Initialization: stc shutdown; stc teardown; ./cleanup.sh"
366 catchError{
367 sh script: runTestCli_py( testName, testCategory ), label: ( "Run Test: ./cli.py run " + testName )
368 }
369 catchError{
370 sh script: concludeRunTest(), label: "Conclude Running Test: ./cleanup.sh; git clean -df"
371 }
372 catchError{
373 // For the Wiki page
374 sh script: cleanAndCopyFiles( pureTestName ), label: "Clean and Copy Files"
375 }
376 }
377 graphs.databaseAndGraph( prop, testName, pureTestName, graphOnly,
378 graph_generator_file, graph_saved_directory )
379 if ( !graphOnly ){
380 sh script: fetchLogs( pureTestName ), label: "Fetch Logs"
381 if ( !isSCPF ){
382 publishToConfluence( prop[ "manualRun" ], prop[ "postResult" ],
383 prop[ "WikiPrefix" ] + "-" + testCategory[ testName ][ 'wikiName' ],
384 workSpace + "/" + testCategory[ testName ][ 'wikiFile' ] )
385 }
386 }
387 }
388 }
389 graphs.postResult( prop, graphOnly, nodeLabel )
390 if ( !graphOnly ){
391 def resultURL = postLogs( testName, prop[ "WikiPrefix" ] )
392 analyzeResult( prop, workSpace, pureTestName, testName, resultURL,
393 isSCPF ? "" : testCategory[ testName ][ 'wikiName' ],
394 isSCPF )
395 }
396 }
397 }
398 }
399 }
400}
401
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -0700402def generateGraphs(){
Jeremy Ronquillo06950992019-07-09 11:16:49 -0700403 if ( category != "SCPF" ){
404 // generate the overall graph of the non SCPF tests.
Jeremy Ronquillo336110a2019-07-11 14:20:40 -0700405 graphs.generateOverallGraph( prop, testsToRun, graphPaths[ "saveDirectory" ], nodeLabel, category )
Jeremy Ronquillobb3001f2019-07-01 12:57:07 -0700406 }
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -0700407}
408
409def sendToSlack(){
Jeremy Ronquillo336110a2019-07-11 14:20:40 -0700410 // send the result of the test to the slack when it is not manually running.
411 // start : start time of the test
412 // isManualRun : string that is whether "false" or "true"
413 // branch : branch of the onos.
414
415 try {
416 if ( prop[ "manualRun" ] == "false" ){
417 end = getCurrentTime()
418 TimeDuration duration = TimeCategory.minus( end, start )
419 // FIXME: for now we disable notifications of normal test results
420 /*
421 slackSend( color: "#5816EE",
422 message: category + "-" + prop[ "WikiPrefix" ] + " tests ended at: " + end.toString() +
423 "\nTime took : " + duration )
424 */
425 }
426 }
427 catch ( all ){
428 }
Jeremy Ronquillo21c29fc2019-06-05 11:15:24 -0700429}