blob: 7e1eb87d6ee6620473462e45984cdb906091aa1e [file] [log] [blame]
khenaidoo1ce37ad2019-03-24 22:07:24 -04001/*
Joey Armstrong5f51f2e2023-01-17 17:06:26 -05002 * Copyright 2018-2023 Open Networking Foundation (ONF) and the ONF Contributors
khenaidoo1ce37ad2019-03-24 22:07:24 -04003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
npujar1d86a522019-11-14 17:11:16 +053016
khenaidoo1ce37ad2019-03-24 22:07:24 -040017package utils
18
khenaidoo2c6a0992019-04-29 13:46:56 -040019import (
Rohan Agrawal31f21802020-06-12 05:38:46 +000020 "context"
Himani Chawlab4c25912020-11-12 17:16:38 +053021 "os"
khenaidood948f772021-08-11 17:49:24 -040022 "os/signal"
23 "syscall"
Himani Chawlab4c25912020-11-12 17:16:38 +053024 "time"
khenaidood948f772021-08-11 17:49:24 -040025
26 "github.com/opencord/voltha-lib-go/v7/pkg/log"
27 "google.golang.org/grpc/codes"
28 "google.golang.org/grpc/peer"
29 "google.golang.org/grpc/status"
Himani Chawlab4c25912020-11-12 17:16:38 +053030)
31
32type contextKey string
33
34func (c contextKey) String() string {
35 return string(c)
36}
37
38var (
39 // RPCContextKey for keeping rpc name as metadata
40 rpcContextKey = contextKey("rpc")
Maninder9a1bc0d2020-10-26 11:34:02 +053041
khenaidood948f772021-08-11 17:49:24 -040042 // endpointKey for keeping the client endpoint making an RPC request
43 endpointKey = contextKey("endpoint")
khenaidoo2c6a0992019-04-29 13:46:56 -040044)
45
khenaidoo442e7c72020-03-10 16:13:48 -040046// ResponseCallback is the function signature for callbacks to execute after a response is received.
Rohan Agrawal31f21802020-06-12 05:38:46 +000047type ResponseCallback func(ctx context.Context, rpc string, response interface{}, reqArgs ...interface{})
khenaidoo442e7c72020-03-10 16:13:48 -040048
npujar1d86a522019-11-14 17:11:16 +053049// DeviceID represent device id attribute
khenaidoo1ce37ad2019-03-24 22:07:24 -040050type DeviceID struct {
npujar1d86a522019-11-14 17:11:16 +053051 ID string
khenaidoo1ce37ad2019-03-24 22:07:24 -040052}
53
npujar1d86a522019-11-14 17:11:16 +053054// LogicalDeviceID rpresent logical device id attribute
khenaidoo1ce37ad2019-03-24 22:07:24 -040055type LogicalDeviceID struct {
npujar1d86a522019-11-14 17:11:16 +053056 ID string
khenaidoo1ce37ad2019-03-24 22:07:24 -040057}
khenaidoo2c6a0992019-04-29 13:46:56 -040058
npujar1d86a522019-11-14 17:11:16 +053059// GetHostName returns host name
khenaidoo631fe542019-05-31 15:44:43 -040060func GetHostName() string {
61 return os.Getenv("HOSTNAME")
62}
63
npujar1d86a522019-11-14 17:11:16 +053064// Response -
Kent Hagerman8da2f1e2019-11-25 17:28:09 -050065type Response struct {
66 *response
67}
68type response struct {
69 err error
70 ch chan struct{}
71 done bool
72}
73
npujar1d86a522019-11-14 17:11:16 +053074// NewResponse -
Kent Hagerman8da2f1e2019-11-25 17:28:09 -050075func NewResponse() Response {
76 return Response{
77 &response{
78 ch: make(chan struct{}),
79 },
80 }
81}
82
A R Karthick5c28f552019-12-11 22:47:44 -080083// Fake a completed response.
84func DoneResponse() Response {
85 r := Response{
86 &response{
87 err: nil,
88 ch: make(chan struct{}),
89 done: true,
90 },
91 }
92 close(r.ch)
93 return r
94}
95
Kent Hagerman8da2f1e2019-11-25 17:28:09 -050096// Error sends a response with the given error. It may only be called once.
97func (r Response) Error(err error) {
98 // if this is called twice, it will panic; this is intentional
99 r.err = err
100 r.done = true
101 close(r.ch)
102}
103
104// Done sends a non-error response unless Error has already been called, in which case this is a no-op.
105func (r Response) Done() {
106 if !r.done {
107 close(r.ch)
108 }
109}
110
khenaidoo2c6a0992019-04-29 13:46:56 -0400111//WaitForNilOrErrorResponses waits on a variadic number of channels for either a nil response or an error
112//response. If an error is received from a given channel then the returned error array will contain that error.
113//The error will be at the index corresponding to the order in which the channel appear in the parameter list.
114//If no errors is found then nil is returned. This method also takes in a timeout in milliseconds. If a
115//timeout is obtained then this function will stop waiting for the remaining responses and abort.
khenaidoo442e7c72020-03-10 16:13:48 -0400116func WaitForNilOrErrorResponses(timeout time.Duration, responses ...Response) []error {
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500117 timedOut := make(chan struct{})
khenaidoo442e7c72020-03-10 16:13:48 -0400118 timer := time.AfterFunc(timeout, func() { close(timedOut) })
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500119 defer timer.Stop()
khenaidoo2c6a0992019-04-29 13:46:56 -0400120
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500121 gotError := false
122 errors := make([]error, 0, len(responses))
123 for _, response := range responses {
124 var err error
125 select {
126 case <-response.ch:
127 // if a response is already available, use it
128 err = response.err
129 default:
130 // otherwise, wait for either a response or a timeout
131 select {
132 case <-response.ch:
133 err = response.err
134 case <-timedOut:
135 err = status.Error(codes.Aborted, "timeout")
khenaidoo2c6a0992019-04-29 13:46:56 -0400136 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400137 }
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500138 gotError = gotError || err != nil
139 errors = append(errors, err)
khenaidoo2c6a0992019-04-29 13:46:56 -0400140 }
141
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500142 if gotError {
khenaidoo2c6a0992019-04-29 13:46:56 -0400143 return errors
144 }
145 return nil
146}
Himani Chawlab4c25912020-11-12 17:16:38 +0530147
148func WithRPCMetadataContext(ctx context.Context, rpcName string) context.Context {
149 ctx = context.WithValue(ctx, rpcContextKey, rpcName)
150 return ctx
151}
152
khenaidood948f772021-08-11 17:49:24 -0400153func WithCoreEndpointContext(ctx context.Context) context.Context {
154 ctx = context.WithValue(ctx, endpointKey, "CORE")
155 return ctx
156}
157
Himani Chawlab4c25912020-11-12 17:16:38 +0530158func GetRPCMetadataFromContext(ctx context.Context) string {
159 if ctx != nil {
160 if val, ok := ctx.Value(rpcContextKey).(string); ok {
161 return val
162 }
163 }
164 return ""
165}
166
167func WithRPCMetadataFromContext(targetCtx, sourceCtx context.Context) context.Context {
168 if sourceCtx != nil {
169 if val, ok := sourceCtx.Value(rpcContextKey).(string); ok {
170 targetCtx = context.WithValue(targetCtx, rpcContextKey, val)
171 }
172 }
173 return targetCtx
174}
175
176func WithSpanAndRPCMetadataFromContext(sourceCtx context.Context) context.Context {
177 targetCtx := context.Background()
178 if sourceCtx != nil {
179 targetCtx = log.WithSpanFromContext(targetCtx, sourceCtx)
180 targetCtx = WithRPCMetadataFromContext(targetCtx, sourceCtx)
181 }
182 return targetCtx
183}
Maninder9a1bc0d2020-10-26 11:34:02 +0530184
khenaidood948f772021-08-11 17:49:24 -0400185func WithRPCMetadataAndEndpointFromContext(targetCtx, sourceCtx context.Context) context.Context {
186 if sourceCtx != nil {
187 targetCtx = WithRPCMetadataFromContext(targetCtx, sourceCtx)
188 targetCtx = WithCoreEndpointContext(targetCtx)
189 }
190 return targetCtx
Maninder9a1bc0d2020-10-26 11:34:02 +0530191}
192
khenaidood948f772021-08-11 17:49:24 -0400193func WithAllMetadataFromContext(sourceCtx context.Context) context.Context {
194 targetCtx := context.Background()
Maninder9a1bc0d2020-10-26 11:34:02 +0530195 if sourceCtx != nil {
khenaidood948f772021-08-11 17:49:24 -0400196 targetCtx = log.WithSpanFromContext(targetCtx, sourceCtx)
197 targetCtx = WithRPCMetadataFromContext(targetCtx, sourceCtx)
198 targetCtx = WithCoreEndpointContext(targetCtx)
199 }
200 return targetCtx
201}
202
203func WithEndpointMetadataFromContext(targetCtx, sourceCtx context.Context) context.Context {
204 if sourceCtx != nil {
205 if val, ok := sourceCtx.Value(endpointKey).(string); ok {
206 targetCtx = context.WithValue(targetCtx, endpointKey, val)
Maninder9a1bc0d2020-10-26 11:34:02 +0530207 }
208 }
209 return targetCtx
210}
211
khenaidood948f772021-08-11 17:49:24 -0400212func WithNewSpanAndRPCMetadataContext(sourceCtx context.Context, rpcName string) context.Context {
213 targetCtx := context.Background()
214 if sourceCtx != nil {
215 sourceEndpoint := ""
216 if p, ok := peer.FromContext(sourceCtx); ok {
217 sourceEndpoint = p.Addr.String()
218 }
219 targetCtx = log.WithSpanFromContext(targetCtx, sourceCtx)
220 targetCtx = context.WithValue(targetCtx, rpcContextKey, rpcName)
221 targetCtx = context.WithValue(targetCtx, endpointKey, sourceEndpoint)
222 }
223 return targetCtx
224}
225
226func GetEndpointMetadataFromContext(ctx context.Context) string {
Maninder9a1bc0d2020-10-26 11:34:02 +0530227 if ctx != nil {
khenaidood948f772021-08-11 17:49:24 -0400228 if val, ok := ctx.Value(endpointKey).(string); ok {
Maninder9a1bc0d2020-10-26 11:34:02 +0530229 return val
230 }
231 }
232 return ""
233}
khenaidood948f772021-08-11 17:49:24 -0400234
235func WaitForExit(ctx context.Context) int {
236 signalChannel := make(chan os.Signal, 1)
237 signal.Notify(signalChannel,
238 syscall.SIGHUP,
239 syscall.SIGINT,
240 syscall.SIGTERM,
241 syscall.SIGQUIT)
242
243 s := <-signalChannel
244 switch s {
245 case syscall.SIGHUP,
246 syscall.SIGINT,
247 syscall.SIGTERM,
248 syscall.SIGQUIT:
249 logger.Infow(ctx, "closing-signal-received", log.Fields{"signal": s})
250 return 0
251 default:
252 logger.Infow(ctx, "unexpected-signal-received", log.Fields{"signal": s})
253 return 1
254 }
255}