blob: 41e4400128f3b4d0693118073d8bc9b39fa56e4c [file] [log] [blame]
khenaidoo1ce37ad2019-03-24 22:07:24 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
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"
yasin sapli5458a1c2021-06-14 22:24:38 +000021 "github.com/opencord/voltha-lib-go/v5/pkg/log"
npujar1d86a522019-11-14 17:11:16 +053022 "google.golang.org/grpc/codes"
23 "google.golang.org/grpc/status"
Himani Chawlab4c25912020-11-12 17:16:38 +053024 "os"
25 "time"
26)
27
28type contextKey string
29
30func (c contextKey) String() string {
31 return string(c)
32}
33
34var (
35 // RPCContextKey for keeping rpc name as metadata
36 rpcContextKey = contextKey("rpc")
Maninder9a1bc0d2020-10-26 11:34:02 +053037
38 // fromTopicContextKey for keeping entity from which operation is requested as metadata
39 fromTopicContextKey = contextKey("fromTopic")
khenaidoo2c6a0992019-04-29 13:46:56 -040040)
41
khenaidoo442e7c72020-03-10 16:13:48 -040042// ResponseCallback is the function signature for callbacks to execute after a response is received.
Rohan Agrawal31f21802020-06-12 05:38:46 +000043type ResponseCallback func(ctx context.Context, rpc string, response interface{}, reqArgs ...interface{})
khenaidoo442e7c72020-03-10 16:13:48 -040044
npujar1d86a522019-11-14 17:11:16 +053045// DeviceID represent device id attribute
khenaidoo1ce37ad2019-03-24 22:07:24 -040046type DeviceID struct {
npujar1d86a522019-11-14 17:11:16 +053047 ID string
khenaidoo1ce37ad2019-03-24 22:07:24 -040048}
49
npujar1d86a522019-11-14 17:11:16 +053050// LogicalDeviceID rpresent logical device id attribute
khenaidoo1ce37ad2019-03-24 22:07:24 -040051type LogicalDeviceID struct {
npujar1d86a522019-11-14 17:11:16 +053052 ID string
khenaidoo1ce37ad2019-03-24 22:07:24 -040053}
khenaidoo2c6a0992019-04-29 13:46:56 -040054
npujar1d86a522019-11-14 17:11:16 +053055// GetHostName returns host name
khenaidoo631fe542019-05-31 15:44:43 -040056func GetHostName() string {
57 return os.Getenv("HOSTNAME")
58}
59
npujar1d86a522019-11-14 17:11:16 +053060// Response -
Kent Hagerman8da2f1e2019-11-25 17:28:09 -050061type Response struct {
62 *response
63}
64type response struct {
65 err error
66 ch chan struct{}
67 done bool
68}
69
npujar1d86a522019-11-14 17:11:16 +053070// NewResponse -
Kent Hagerman8da2f1e2019-11-25 17:28:09 -050071func NewResponse() Response {
72 return Response{
73 &response{
74 ch: make(chan struct{}),
75 },
76 }
77}
78
A R Karthick5c28f552019-12-11 22:47:44 -080079// Fake a completed response.
80func DoneResponse() Response {
81 r := Response{
82 &response{
83 err: nil,
84 ch: make(chan struct{}),
85 done: true,
86 },
87 }
88 close(r.ch)
89 return r
90}
91
Kent Hagerman8da2f1e2019-11-25 17:28:09 -050092// Error sends a response with the given error. It may only be called once.
93func (r Response) Error(err error) {
94 // if this is called twice, it will panic; this is intentional
95 r.err = err
96 r.done = true
97 close(r.ch)
98}
99
100// Done sends a non-error response unless Error has already been called, in which case this is a no-op.
101func (r Response) Done() {
102 if !r.done {
103 close(r.ch)
104 }
105}
106
khenaidoo2c6a0992019-04-29 13:46:56 -0400107//WaitForNilOrErrorResponses waits on a variadic number of channels for either a nil response or an error
108//response. If an error is received from a given channel then the returned error array will contain that error.
109//The error will be at the index corresponding to the order in which the channel appear in the parameter list.
110//If no errors is found then nil is returned. This method also takes in a timeout in milliseconds. If a
111//timeout is obtained then this function will stop waiting for the remaining responses and abort.
khenaidoo442e7c72020-03-10 16:13:48 -0400112func WaitForNilOrErrorResponses(timeout time.Duration, responses ...Response) []error {
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500113 timedOut := make(chan struct{})
khenaidoo442e7c72020-03-10 16:13:48 -0400114 timer := time.AfterFunc(timeout, func() { close(timedOut) })
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500115 defer timer.Stop()
khenaidoo2c6a0992019-04-29 13:46:56 -0400116
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500117 gotError := false
118 errors := make([]error, 0, len(responses))
119 for _, response := range responses {
120 var err error
121 select {
122 case <-response.ch:
123 // if a response is already available, use it
124 err = response.err
125 default:
126 // otherwise, wait for either a response or a timeout
127 select {
128 case <-response.ch:
129 err = response.err
130 case <-timedOut:
131 err = status.Error(codes.Aborted, "timeout")
khenaidoo2c6a0992019-04-29 13:46:56 -0400132 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400133 }
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500134 gotError = gotError || err != nil
135 errors = append(errors, err)
khenaidoo2c6a0992019-04-29 13:46:56 -0400136 }
137
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500138 if gotError {
khenaidoo2c6a0992019-04-29 13:46:56 -0400139 return errors
140 }
141 return nil
142}
Himani Chawlab4c25912020-11-12 17:16:38 +0530143
144func WithRPCMetadataContext(ctx context.Context, rpcName string) context.Context {
145 ctx = context.WithValue(ctx, rpcContextKey, rpcName)
146 return ctx
147}
148
149func GetRPCMetadataFromContext(ctx context.Context) string {
150 if ctx != nil {
151 if val, ok := ctx.Value(rpcContextKey).(string); ok {
152 return val
153 }
154 }
155 return ""
156}
157
158func WithRPCMetadataFromContext(targetCtx, sourceCtx context.Context) context.Context {
159 if sourceCtx != nil {
160 if val, ok := sourceCtx.Value(rpcContextKey).(string); ok {
161 targetCtx = context.WithValue(targetCtx, rpcContextKey, val)
162 }
163 }
164 return targetCtx
165}
166
167func WithSpanAndRPCMetadataFromContext(sourceCtx context.Context) context.Context {
168 targetCtx := context.Background()
169 if sourceCtx != nil {
170 targetCtx = log.WithSpanFromContext(targetCtx, sourceCtx)
171 targetCtx = WithRPCMetadataFromContext(targetCtx, sourceCtx)
172 }
173 return targetCtx
174}
Maninder9a1bc0d2020-10-26 11:34:02 +0530175
176func WithFromTopicMetadataContext(ctx context.Context, fromTopic string) context.Context {
177 ctx = context.WithValue(ctx, fromTopicContextKey, fromTopic)
178 return ctx
179}
180
181func WithFromTopicMetadataFromContext(targetCtx, sourceCtx context.Context) context.Context {
182 if sourceCtx != nil {
183 if val, ok := sourceCtx.Value(fromTopicContextKey).(string); ok {
184 targetCtx = context.WithValue(targetCtx, fromTopicContextKey, val)
185 }
186 }
187 return targetCtx
188}
189
190func GetFromTopicMetadataFromContext(ctx context.Context) string {
191 if ctx != nil {
192 if val, ok := ctx.Value(fromTopicContextKey).(string); ok {
193 return val
194 }
195 }
196 return ""
197}