blob: 3c441da19747e1b007f6df85e573adb9d640c3ba [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{
Joey Armstrong7987c112022-12-05 12:42:43 -05009 // Cannot rely on a stack trace due to jenkins manipulation
Joey Armstrong3d444c62022-11-26 21:42:20 -050010 String src = 'vars/waitForAdapters.groovy'
11 String iam = [src, func].join('::')
Joey Armstrongf0c76902022-11-28 17:14:45 -050012 return iam
Joey Armstrong3d444c62022-11-26 21:42:20 -050013}
Andrea Campanella45b8eb72021-09-28 10:50:01 +020014
Joey Armstrong3d444c62022-11-26 21:42:20 -050015// -----------------------------------------------------------------------
16// -----------------------------------------------------------------------
17def getAdapters()
18{
19 String iam = getIam('getAdapters')
20
21 def adapters = ""
22 try
23 {
24 adapters = sh (
25 script: 'voltctl adapter list --format "{{gosince .LastCommunication}}"',
26 returnStdout: true,
27 ).trim()
28 }
29 catch (err)
30 {
31 // in some older versions of voltctl the command results in
32 // ERROR: Unexpected error while attempting to format results
33 // as table : template: output:1: function "gosince" not defined
34 // if that's the case we won't be able to check the timestamp so
35 // it's useless to wait
36 println("voltctl can't parse LastCommunication, skip waiting")
Joey Armstrong14a67a12022-11-28 12:28:23 -050037 adapters = 'SKIP' // why not just retry a few times (?)
Joey Armstrong3d444c62022-11-26 21:42:20 -050038 }
39
40 print("** ${iam}: returned $adapters")
41 return adapters
42}
43
44// -----------------------------------------------------------------------
Joey Armstrong14a67a12022-11-28 12:28:23 -050045// Intent: Interrogate adapter states to determine if we should continue
46// looping waiting for adapter to start. Passed values must all be
47// Integral, Valid and within range to assert we are done waiting.
48// Function will return incomplete at the first sign of an invalid value.
49// -----------------------------------------------------------------------
50// NOTE: list.find{} was replaced with a for loop to simplify
51// early loop termination and return.
52// -----------------------------------------------------------------------
53// RETURN: scalar
54// 'DOUBLE-DIGIT' - value exceeds 0-5sec response window.
55// 'NON-NUMERIC' - garbage in the stream (letter, symbol, ctrl)
56// 'NULL' - empty or null string detected
57// 'VALID' - succes, all adapters are functional
58// 'SIX-NINE' - detected a single numeric digit between 6 and 9.
59// -----------------------------------------------------------------------
60def getAdaptersState(String adapters0)
61{
62 String iam = getIam('getAdaptersState')
63 Boolean debug = true // for now
64
65 def adapters = adapters0.split('\n')
66
67 String ans = null
68 def found = []
69 for(i=0; i<adapters.size(); i++)
70 {
Joey Armstrong3d56fee2022-11-28 16:02:41 -050071 String elapsed = adapters[i]
Joey Armstrong14a67a12022-11-28 12:28:23 -050072 if (debug)
Joey Armstrong7987c112022-12-05 12:42:43 -050073 {
Joey Armstrong14a67a12022-11-28 12:28:23 -050074 println("** ${iam} Checking elapsed[$i]: $elapsed")
Joey Armstrong7987c112022-12-05 12:42:43 -050075 }
Joey Armstrong14a67a12022-11-28 12:28:23 -050076
77 if (! elapsed) // empty string or null
78 {
79 ans = 'NULL'
80 break
81 }
82
83 Integer size = elapsed.length()
84 if (size > 2) // 463765h58m52(s)
85 {
86 // higlander: there can be only one
87 ans = 'DOUBLE-DIGIT'
88 break
89 }
90
91 if (elapsed.endsWith('s'))
92 {
93 // safer than replaceAll('s'): ssss1s => 1
94 elapsed = elapsed.substring(0, size-1)
95 }
96
97 // Line noise guard: 'a', 'f', '#', '!'
98 if (! elapsed.isInteger())
99 {
100 ans = 'NON-NUMERIC'
101 break
102 }
103
104 // Is value in range:
105 // discard negative integers as just plain wonky.
106 // HMMM(?): zero/no latency response is kinda odd to include imho.
107 Integer val = elapsed.toInteger()
108 if (5 >= val && val >= 0)
109 {
110 found.add(elapsed)
111 }
112 else
113 {
114 ans = 'SIX-NINE'
115 break
116 }
117 } // for()
118
119 // Declare success IFF all values are:
120 // o integral
121 // o valid
122 // o within range
123 if (ans == null)
124 {
125 Integer got = found.size()
126 Integer exp = adapters.size()
127 ans = (! exp) ? 'NO-ADAPTERS'
128 : (got == exp) ? 'VALID'
129 : 'CONTINUE'
130 }
131
Joey Armstrong3d56fee2022-11-28 16:02:41 -0500132 if (debug)
Joey Armstrong7987c112022-12-05 12:42:43 -0500133 {
Joey Armstrong3d56fee2022-11-28 16:02:41 -0500134 println("** ${iam} return: [$ans]")
Joey Armstrong7987c112022-12-05 12:42:43 -0500135 }
Joey Armstrong14a67a12022-11-28 12:28:23 -0500136 return ans
137} // getAdaptersState
138
139// -----------------------------------------------------------------------
Joey Armstrong3d444c62022-11-26 21:42:20 -0500140// -----------------------------------------------------------------------
141def process(Map config)
142{
143 String iam = getIam('process')
Joey Armstrong96158a92022-11-25 10:36:06 -0500144 println("** ${iam}: ENTER")
145
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200146 def defaultConfig = [
147 volthaNamespace: "voltha",
Matteo Scandolo721d08b2021-09-30 17:42:40 -0700148 stackName: "voltha",
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200149 adaptersToWait: 2,
150 ]
151
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200152 def cfg = defaultConfig + config
153
Andrea Campanella365ea1e2021-09-28 10:50:01 +0200154 if (cfg.adaptersToWait == 0){
155 //no need to wait
156 println "No need to wait for adapters to be registered"
157 return
158 }
159
Joey Armstrong3d444c62022-11-26 21:42:20 -0500160 println("** ${iam}: Wait for adapters to be registered")
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200161
162 // guarantee that at least the specified number of adapters are registered with VOLTHA before proceeding
163 sh """
164 set +x
Matteo Scandolo721d08b2021-09-30 17:42:40 -0700165 _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 +0200166 """
167
168 sh """
169 set +x
170 adapters=\$(voltctl adapter list -q | wc -l)
171 while [[ \$adapters -lt ${cfg.adaptersToWait} ]]; do
172 sleep 5
173 adapters=\$(voltctl adapter list -q | wc -l)
174 done
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200175 """
176
Matteo Scandolo28722ea2021-10-01 15:48:42 -0700177 // NOTE that we need to wait for LastCommunication to be equal or shorter that 5s
178 // as that means the core can talk to the adapters
179 // if voltctl can't read LastCommunication we skip this check
Joey Armstrong3d444c62022-11-26 21:42:20 -0500180
181 println("** ${iam}: Wait for adapter LastCommunication")
Joey Armstrong14a67a12022-11-28 12:28:23 -0500182 Integer countdown = 60 * 10
183 while (true)
Joey Armstrong3d444c62022-11-26 21:42:20 -0500184 {
185 sleep 1
186 def adapters = getAdapters()
Joey Armstrong14a67a12022-11-28 12:28:23 -0500187 // Why are we not failing hard on this condition ?
188 // Adapters in an unknown state == testing: roll the dice
Joey Armstrong3d444c62022-11-26 21:42:20 -0500189 if (adapters == 'SKIP') { break }
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200190
Joey Armstrong14a67a12022-11-28 12:28:23 -0500191 def state = getAdaptersState(adapters)
192 if (state == 'VALID') { break } // IFF
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200193
Joey Armstrong14a67a12022-11-28 12:28:23 -0500194 // ----------------------------------------------------------
195 // Excessive timeout but unsure where startup time boundry is
196 // [TODO] profile for a baseline
197 // [TODO] groovy.transform.TimedInterrupt
198 // ----------------------------------------------------------
199 countdown -= 1
200 if (1 > countdown)
Joey Armstrong7987c112022-12-05 12:42:43 -0500201 {
Joey Armstrong14a67a12022-11-28 12:28:23 -0500202 throw new Exception("ERROR: Timed out waiting on adapter startup")
Joey Armstrong7987c112022-12-05 12:42:43 -0500203 }
Matteo Scandolo28722ea2021-10-01 15:48:42 -0700204 }
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200205
Joey Armstrongf0c76902022-11-28 17:14:45 -0500206 println("** ${iam}: Tearing down port forwarding")
Joey Armstrong3d444c62022-11-26 21:42:20 -0500207 sh("""
Joey Armstrong3d444c62022-11-26 21:42:20 -0500208 ps aux \
209 | grep port-forw \
210 | grep -v grep \
211 | awk '{print \$2}' \
212 | xargs --no-run-if-empty kill -9 || true
213 """)
Joey Armstrong96158a92022-11-25 10:36:06 -0500214
215 println("** ${iam}: LEAVE")
Joey Armstrong3d444c62022-11-26 21:42:20 -0500216 return
Andrea Campanella45b8eb72021-09-28 10:50:01 +0200217}
Joey Armstrong3d444c62022-11-26 21:42:20 -0500218
219// -----------------------------------------------------------------------
220// -----------------------------------------------------------------------
221def call(Map config)
222{
Joey Armstrong7987c112022-12-05 12:42:43 -0500223 String iam = getIam('main')
Joey Armstrong3d444c62022-11-26 21:42:20 -0500224 println("** ${iam}: ENTER")
225
226 if (!config) {
227 config = [:]
228 }
229
230 try
231 {
232 process(config)
233 }
234 catch (Exception err)
235 {
236 println("** ${iam}: EXCEPTION ${err}")
237 throw err
238 }
239 finally
240 {
241 println("** ${iam}: LEAVE")
242 }
243 return
244}
245
246// EOF