blob: 5d2f2a85ac088c85b77bf67e97441be9cb4562f2 [file] [log] [blame]
Joey Armstrong96158a92022-11-25 10:36:06 -05001#!/usr/bin/env groovy
Joey Armstrong3d444c62022-11-26 21:42:20 -05002// -----------------------------------------------------------------------
3// -----------------------------------------------------------------------
Joey Armstrong96158a92022-11-25 10:36:06 -05004
Joey Armstrong3d444c62022-11-26 21:42:20 -05005// -----------------------------------------------------------------------
6// -----------------------------------------------------------------------
7def getIam(String func)
8{
9 String src = 'vars/waitForAdapters.groovy'
10 String iam = [src, func].join('::')
11 return
12}
Andrea Campanella45b8eb72021-09-28 10:50:01 +020013
Joey Armstrong3d444c62022-11-26 21:42:20 -050014// -----------------------------------------------------------------------
15// -----------------------------------------------------------------------
16def getAdapters()
17{
18 String iam = getIam('getAdapters')
19
20 def adapters = ""
21 try
22 {
23 adapters = sh (
24 script: 'voltctl adapter list --format "{{gosince .LastCommunication}}"',
25 returnStdout: true,
26 ).trim()
27 }
28 catch (err)
29 {
30 // in some older versions of voltctl the command results in
31 // ERROR: Unexpected error while attempting to format results
32 // as table : template: output:1: function "gosince" not defined
33 // if that's the case we won't be able to check the timestamp so
34 // it's useless to wait
35 println("voltctl can't parse LastCommunication, skip waiting")
Joey Armstrong14a67a12022-11-28 12:28:23 -050036 adapters = 'SKIP' // why not just retry a few times (?)
Joey Armstrong3d444c62022-11-26 21:42:20 -050037 }
38
39 print("** ${iam}: returned $adapters")
40 return adapters
41}
42
43// -----------------------------------------------------------------------
Joey Armstrong14a67a12022-11-28 12:28:23 -050044// Intent: Interrogate adapter states to determine if we should continue
45// looping waiting for adapter to start. Passed values must all be
46// Integral, Valid and within range to assert we are done waiting.
47// Function will return incomplete at the first sign of an invalid value.
48// -----------------------------------------------------------------------
49// NOTE: list.find{} was replaced with a for loop to simplify
50// early loop termination and return.
51// -----------------------------------------------------------------------
52// RETURN: scalar
53// 'DOUBLE-DIGIT' - value exceeds 0-5sec response window.
54// 'NON-NUMERIC' - garbage in the stream (letter, symbol, ctrl)
55// 'NULL' - empty or null string detected
56// 'VALID' - succes, all adapters are functional
57// 'SIX-NINE' - detected a single numeric digit between 6 and 9.
58// -----------------------------------------------------------------------
59def getAdaptersState(String adapters0)
60{
61 String iam = getIam('getAdaptersState')
62 Boolean debug = true // for now
63
64 def adapters = adapters0.split('\n')
65
66 String ans = null
67 def found = []
68 for(i=0; i<adapters.size(); i++)
69 {
70 String elapsed = args[i]
71 if (debug)
72 println("** ${iam} Checking elapsed[$i]: $elapsed")
73
74 if (! elapsed) // empty string or null
75 {
76 ans = 'NULL'
77 break
78 }
79
80 Integer size = elapsed.length()
81 if (size > 2) // 463765h58m52(s)
82 {
83 // higlander: there can be only one
84 ans = 'DOUBLE-DIGIT'
85 break
86 }
87
88 if (elapsed.endsWith('s'))
89 {
90 // safer than replaceAll('s'): ssss1s => 1
91 elapsed = elapsed.substring(0, size-1)
92 }
93
94 // Line noise guard: 'a', 'f', '#', '!'
95 if (! elapsed.isInteger())
96 {
97 ans = 'NON-NUMERIC'
98 break
99 }
100
101 // Is value in range:
102 // discard negative integers as just plain wonky.
103 // HMMM(?): zero/no latency response is kinda odd to include imho.
104 Integer val = elapsed.toInteger()
105 if (5 >= val && val >= 0)
106 {
107 found.add(elapsed)
108 }
109 else
110 {
111 ans = 'SIX-NINE'
112 break
113 }
114 } // for()
115
116 // Declare success IFF all values are:
117 // o integral
118 // o valid
119 // o within range
120 if (ans == null)
121 {
122 Integer got = found.size()
123 Integer exp = adapters.size()
124 ans = (! exp) ? 'NO-ADAPTERS'
125 : (got == exp) ? 'VALID'
126 : 'CONTINUE'
127 }
128
129 println("** ${iam} return: [$ans]")
130 return ans
131} // getAdaptersState
132
133// -----------------------------------------------------------------------
Joey Armstrong3d444c62022-11-26 21:42:20 -0500134// -----------------------------------------------------------------------
135def process(Map config)
136{
137 String iam = getIam('process')
Joey Armstrong96158a92022-11-25 10:36:06 -0500138 println("** ${iam}: ENTER")
139
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200140 def defaultConfig = [
141 volthaNamespace: "voltha",
Matteo Scandolo721d08b2021-09-30 17:42:40 -0700142 stackName: "voltha",
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200143 adaptersToWait: 2,
144 ]
145
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200146 def cfg = defaultConfig + config
147
Andrea Campanella365ea1e2021-09-28 10:50:01 +0200148 if (cfg.adaptersToWait == 0){
149 //no need to wait
150 println "No need to wait for adapters to be registered"
151 return
152 }
153
Joey Armstrong3d444c62022-11-26 21:42:20 -0500154 println("** ${iam}: Wait for adapters to be registered")
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200155
156 // guarantee that at least the specified number of adapters are registered with VOLTHA before proceeding
157 sh """
158 set +x
Matteo Scandolo721d08b2021-09-30 17:42:40 -0700159 _TAG="voltha-voltha-api" bash -c "while true; do kubectl port-forward --address 0.0.0.0 -n ${cfg.volthaNamespace} svc/${cfg.stackName}-voltha-api 55555:55555; done"&
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200160 """
161
162 sh """
163 set +x
164 adapters=\$(voltctl adapter list -q | wc -l)
165 while [[ \$adapters -lt ${cfg.adaptersToWait} ]]; do
166 sleep 5
167 adapters=\$(voltctl adapter list -q | wc -l)
168 done
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200169 """
170
Matteo Scandolo28722ea2021-10-01 15:48:42 -0700171 // NOTE that we need to wait for LastCommunication to be equal or shorter that 5s
172 // as that means the core can talk to the adapters
173 // if voltctl can't read LastCommunication we skip this check
Joey Armstrong3d444c62022-11-26 21:42:20 -0500174
175 println("** ${iam}: Wait for adapter LastCommunication")
Joey Armstrong14a67a12022-11-28 12:28:23 -0500176 Integer countdown = 60 * 10
177 while (true)
Joey Armstrong3d444c62022-11-26 21:42:20 -0500178 {
179 sleep 1
180 def adapters = getAdapters()
Joey Armstrong14a67a12022-11-28 12:28:23 -0500181 // Why are we not failing hard on this condition ?
182 // Adapters in an unknown state == testing: roll the dice
Joey Armstrong3d444c62022-11-26 21:42:20 -0500183 if (adapters == 'SKIP') { break }
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200184
Joey Armstrong14a67a12022-11-28 12:28:23 -0500185 def state = getAdaptersState(adapters)
186 if (state == 'VALID') { break } // IFF
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200187
Joey Armstrong14a67a12022-11-28 12:28:23 -0500188 // ----------------------------------------------------------
189 // Excessive timeout but unsure where startup time boundry is
190 // [TODO] profile for a baseline
191 // [TODO] groovy.transform.TimedInterrupt
192 // ----------------------------------------------------------
193 countdown -= 1
194 if (1 > countdown)
195 throw new Exception("ERROR: Timed out waiting on adapter startup")
Matteo Scandolo28722ea2021-10-01 15:48:42 -0700196 }
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200197
Joey Armstrong3d444c62022-11-26 21:42:20 -0500198 println("** ${iam}: Wait for adapter LastCommunication")
199 sh("""
Matteo Scandolo28722ea2021-10-01 15:48:42 -0700200 set +x
Joey Armstronga1915cf2022-11-26 15:58:49 -0500201 pgrep --list-full port-forw
Joey Armstrong96158a92022-11-25 10:36:06 -0500202
Joey Armstrong3d444c62022-11-26 21:42:20 -0500203 ps aux \
204 | grep port-forw \
205 | grep -v grep \
206 | awk '{print \$2}' \
207 | xargs --no-run-if-empty kill -9 || true
208 """)
Joey Armstrong96158a92022-11-25 10:36:06 -0500209
210 println("** ${iam}: LEAVE")
Joey Armstrong3d444c62022-11-26 21:42:20 -0500211 return
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200212}
Joey Armstrong3d444c62022-11-26 21:42:20 -0500213
214// -----------------------------------------------------------------------
215// -----------------------------------------------------------------------
216def call(Map config)
217{
218 String iam = getIam('process')
219 println("** ${iam}: ENTER")
220
221 if (!config) {
222 config = [:]
223 }
224
225 try
226 {
227 process(config)
228 }
229 catch (Exception err)
230 {
231 println("** ${iam}: EXCEPTION ${err}")
232 throw err
233 }
234 finally
235 {
236 println("** ${iam}: LEAVE")
237 }
238 return
239}
240
241// EOF