blob: 8394c60d8080b6311305a85bbd808b4275f2135f [file] [log] [blame]
sslobodr392ebd52019-01-18 12:41:49 -05001/*
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 */
16// gRPC affinity router with active/active backends
17
18package afrouter
19
20import (
21 "fmt"
22 "errors"
23 "google.golang.org/grpc"
24 "google.golang.org/grpc/metadata"
25 "github.com/opencord/voltha-go/common/log"
26)
27
28const NoMeta = "nometa"
29
30type MethodRouter struct {
31 name string
32 service string
33 mthdRt map[string]map[string]Router // map of [metadata][method]
34}
35
36func newMethodRouter(config *RouterConfig) (Router, error) {
37 mr := MethodRouter{name:config.Name,service:config.ProtoService,mthdRt:make(map[string]map[string]Router)}
38 mr.mthdRt[NoMeta] = make(map[string]Router) // For routes not needing metadata (all expcept binding at this time)
39 if len(config.Routes) == 0 {
40 return nil, errors.New(fmt.Sprintf("Router %s must have at least one route", config.Name))
41 }
sslobodr5f0b5a32019-01-24 07:45:19 -050042 for _,rtv := range config.Routes {
sslobodr392ebd52019-01-18 12:41:49 -050043 var idx1 string
44 r,err := newRouter(config, &rtv)
45 if err != nil {
46 return nil, err
47 }
48 if rtv.Type == "binding" {
49 idx1 = rtv.Binding.Field
50 if _,ok := mr.mthdRt[idx1]; ok == false { // /First attempt on this key
51 mr.mthdRt[idx1] = make(map[string]Router)
52 }
53 } else {
54 idx1 = NoMeta
55 }
56 switch len(rtv.Methods) {
57 case 0:
58 return nil, errors.New(fmt.Sprintf("Route for router %s must have at least one method", config.Name))
59 case 1:
60 if rtv.Methods[0] == "*" {
61 return r, nil
62 } else {
63 log.Debugf("Setting router '%s' for single method '%s'",r.Name(),rtv.Methods[0])
64 if _,ok := mr.mthdRt[""][rtv.Methods[0]]; ok == false {
65 mr.mthdRt[idx1][rtv.Methods[0]] = r
66 } else {
67 err := errors.New(fmt.Sprintf("Attempt to define method %s for 2 routes: %s & %s", rtv.Methods[0],
68 r.Name(), mr.mthdRt[idx1][rtv.Methods[0]].Name()))
69 log.Debug(err)
70 return mr, err
71 }
72 }
73 default:
sslobodr5f0b5a32019-01-24 07:45:19 -050074 for _,m := range rtv.Methods {
sslobodr392ebd52019-01-18 12:41:49 -050075 log.Debugf("Setting router '%s' for method '%s'",r.Name(),m)
76 if _,ok := mr.mthdRt[idx1][m]; ok == false {
77 mr.mthdRt[idx1][m] = r
78 } else {
79 err := errors.New(fmt.Sprintf("Attempt to define method %s for 2 routes: %s & %s", rtv.Methods[0],
80 r.Name(), mr.mthdRt[idx1][m].Name()))
81 log.Debug(err)
82 return mr, err
83 }
84 }
85 }
86 }
87
88 return mr, nil
89}
90
91func (mr MethodRouter) Name() string {
92 return mr.name
93}
94
95func (mr MethodRouter) Service() string {
96 return mr.service
97}
98
99func (mr MethodRouter) GetMetaKeyVal(serverStream grpc.ServerStream) (string,string,error) {
100 var rtrnK string = NoMeta
101 var rtrnV string = ""
102
103 // Get the metadata from the server stream
104 md, ok := metadata.FromIncomingContext(serverStream.Context())
105 if !ok {
106 return rtrnK, rtrnV, errors.New("Could not get a server stream metadata")
107 }
108
109 // Determine if one of the method routing keys exists in the metadata
sslobodr5f0b5a32019-01-24 07:45:19 -0500110 for k,_ := range mr.mthdRt {
sslobodr392ebd52019-01-18 12:41:49 -0500111 if _,ok := md[k]; ok == true {
112 rtrnV = md[k][0]
113 rtrnK = k
114 break
115 }
116 }
117 return rtrnK,rtrnV,nil
118
119}
120
121func (mr MethodRouter) ReplyHandler(sel interface{}) error {
122 switch sl := sel.(type) {
123 case *sbFrame:
124 if r,ok := mr.mthdRt[NoMeta][sl.method]; ok == true {
125 return r.ReplyHandler(sel)
126 }
127 // TODO: this case should also be an error
128 default: //TODO: This should really be a big error
129 // A reply handler should only be called on the sbFrame
130 return nil
131 }
132 return nil
133}
134
135func (mr MethodRouter) Route(sel interface{}) *backend {
136 switch sl := sel.(type) {
137 case *nbFrame:
138 if r,ok := mr.mthdRt[sl.metaKey][sl.mthdSlice[REQ_METHOD]]; ok == true {
139 return r.Route(sel)
140 }
141 log.Errorf("Attept to route on non-existent method '%s'", sl.mthdSlice[REQ_METHOD])
142 return nil
143 default:
144 return nil
145 }
146 return nil
147}
148
149func (mr MethodRouter) BackendCluster(mthd string, metaKey string) (*backendCluster,error) {
150 if r,ok := mr.mthdRt[metaKey][mthd]; ok == true {
151 return r.BackendCluster(mthd, metaKey)
152 }
153 err := errors.New(fmt.Sprintf("No backend cluster exists for method '%s' using meta key '%s'", mthd,metaKey))
154 log.Error(err)
155 return nil, err
156}
157
158func (mr MethodRouter) FindBackendCluster(beName string) *backendCluster {
159 for _,meta := range mr.mthdRt {
160 for _,r := range meta {
161 if rtrn := r.FindBackendCluster(beName); rtrn != nil {
162 return rtrn
163 }
164 }
165 }
166 return nil
167}