blob: 78415543c40ff2e8774962da446fa2e67aa905e9 [file] [log] [blame]
Scott Baker112b0d42019-08-22 08:32:26 -07001/*
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
17package afrouter
18
19/* Source-Router
20
21 This router implements source routing where the caller identifies the
22 component the message should be routed to. The `RouteField` should be
23 configured with the gRPC field name to inspect to determine the
24 destination. This field is assumed to be a string. That string will
25 then be used to identify a particular connection on a particular
26 backend.
27
28 The source-router must be configured with a backend cluster, as all routers
29 must identify a backend cluster. However, that backend cluster
30 is merely a placeholder and is not used by the source-router. The
31 source-router's Route() function will return whatever backend cluster is
32 specified by the `RouteField`.
33*/
34
35import (
36 "errors"
37 "fmt"
38 "github.com/golang/protobuf/proto"
Scott Baker112b0d42019-08-22 08:32:26 -070039 "github.com/opencord/voltha-go/common/log"
40 "google.golang.org/grpc"
Scott Baker112b0d42019-08-22 08:32:26 -070041 "regexp"
42 "strconv"
43)
44
45type SourceRouter struct {
46 name string
47 //association associationType
Scott Baker056aa952019-09-23 17:25:04 -070048 routingField string
49 grpcService string
50 methodMap map[string]byte
51 cluster *cluster
Scott Baker112b0d42019-08-22 08:32:26 -070052}
53
54func newSourceRouter(rconf *RouterConfig, config *RouteConfig) (Router, error) {
55 var err error = nil
56 var rtrn_err = false
57 var pkg_re = regexp.MustCompile(`^(\.[^.]+\.)(.+)$`)
58 // Validate the configuration
59
60 // A name must exist
61 if config.Name == "" {
62 log.Error("A router 'name' must be specified")
63 rtrn_err = true
64 }
65
66 if rconf.ProtoPackage == "" {
67 log.Error("A 'package' must be specified")
68 rtrn_err = true
69 }
70
71 if rconf.ProtoService == "" {
72 log.Error("A 'service' must be specified")
73 rtrn_err = true
74 }
75
76 if config.RouteField == "" {
77 log.Error("A 'routing_field' must be specified")
78 rtrn_err = true
79 }
80
81 // TODO The overrieds section is currently not being used
82 // so the router will route all methods based on the
83 // routing_field. This needs to be added so that methods
84 // can have different routing fields.
85 dr := SourceRouter{
86 name: config.Name,
87 grpcService: rconf.ProtoService,
88 methodMap: make(map[string]byte),
89 }
90
Scott Baker112b0d42019-08-22 08:32:26 -070091 // Build the routing structure based on the loaded protobuf
92 // descriptor file and the config information.
93 type key struct {
94 method string
95 field string
96 }
Scott Baker056aa952019-09-23 17:25:04 -070097 var fieldNumberLookup = make(map[key]byte)
98 for _, f := range rconf.protoDescriptor.File {
Scott Baker112b0d42019-08-22 08:32:26 -070099 // Build a temporary map of message types by name.
100 for _, m := range f.MessageType {
101 for _, fld := range m.Field {
102 log.Debugf("Processing message '%s', field '%s'", *m.Name, *fld.Name)
Scott Baker056aa952019-09-23 17:25:04 -0700103 fieldNumberLookup[key{*m.Name, *fld.Name}] = byte(*fld.Number)
Scott Baker112b0d42019-08-22 08:32:26 -0700104 }
105 }
106 }
Scott Baker056aa952019-09-23 17:25:04 -0700107 for _, f := range rconf.protoDescriptor.File {
Scott Baker112b0d42019-08-22 08:32:26 -0700108 if *f.Package == rconf.ProtoPackage {
109 for _, s := range f.Service {
110 if *s.Name == rconf.ProtoService {
111 log.Debugf("Loading package data '%s' for service '%s' for router '%s'", *f.Package, *s.Name, dr.name)
112 // Now create a map keyed by method name with the value being the
113 // field number of the route selector.
114 var ok bool
115 for _, m := range s.Method {
116 // Find the input type in the messages and extract the
117 // field number and save it for future reference.
118 log.Debugf("Processing method '%s'", *m.Name)
119 // Determine if this is a method we're supposed to be processing.
120 if needMethod(*m.Name, config) {
121 log.Debugf("Enabling method '%s'", *m.Name)
122 pkg_methd := pkg_re.FindStringSubmatch(*m.InputType)
123 if pkg_methd == nil {
124 log.Errorf("Regular expression didn't match input type '%s'", *m.InputType)
125 rtrn_err = true
126 }
127 // The input type has the package name prepended to it. Remove it.
128 //in := (*m.InputType)[len(rconf.ProtoPackage)+2:]
129 in := pkg_methd[PKG_MTHD_MTHD]
Scott Baker056aa952019-09-23 17:25:04 -0700130 dr.methodMap[*m.Name], ok = fieldNumberLookup[key{in, config.RouteField}]
Scott Baker112b0d42019-08-22 08:32:26 -0700131 if !ok {
132 log.Errorf("Method '%s' has no field named '%s' in it's parameter message '%s'",
133 *m.Name, config.RouteField, in)
134 rtrn_err = true
135 }
136 }
137 }
138 }
139 }
140 }
141 }
142
143 // We need to pick a cluster, because server will call cluster.handler. The choice we make doesn't
144 // matter, as we can return a different cluster from Route().
145 ok := true
146 if dr.cluster, ok = clusters[config.backendCluster.Name]; !ok {
147 if dr.cluster, err = newBackendCluster(config.backendCluster); err != nil {
148 log.Errorf("Could not create a backend for router %s", config.Name)
149 rtrn_err = true
150 }
151 }
152
153 if rtrn_err {
154 return dr, errors.New(fmt.Sprintf("Failed to create a new router '%s'", dr.name))
155 }
156
157 return dr, nil
158}
159
160func (ar SourceRouter) Service() string {
161 return ar.grpcService
162}
163
164func (ar SourceRouter) Name() string {
165 return ar.name
166}
167
168func (ar SourceRouter) skipField(data *[]byte, idx *int) error {
169 switch (*data)[*idx] & 3 {
170 case 0: // Varint
171 // skip the field number/type
172 *idx++
173 // if the msb is set, then more bytes to follow
174 for (*data)[*idx] >= 128 {
175 *idx++
176 }
177 // the last byte doesn't have the msb set
178 *idx++
179 case 1: // 64 bit
180 *idx += 9
181 case 2: // Length delimited
182 // skip the field number / type
183 *idx++
184 // read a varint that tells length of string
185 b := proto.NewBuffer((*data)[*idx:])
186 t, _ := b.DecodeVarint()
187 // skip the length varint and the string bytes
188 // TODO: This assumes the varint was one byte long -- max string length is 127 bytes
189 *idx += int(t) + 1
190 case 3: // Deprecated
191 case 4: // Deprecated
192 case 5: // 32 bit
193 *idx += 5
194 }
195 return nil
196}
197
198func (ar SourceRouter) decodeProtoField(payload []byte, fieldId byte) (string, error) {
199 idx := 0
200 b := proto.NewBuffer([]byte{})
201 //b.DebugPrint("The Buffer", payload)
202 for { // Find the route selector field
203 log.Debugf("Decoding source value attributeNumber: %d from %v at index %d", fieldId, payload, idx)
204 log.Debugf("Attempting match with payload: %d, methodTable: %d", payload[idx], fieldId)
205 if payload[idx]>>3 == fieldId {
206 log.Debugf("Method match with payload: %d, methodTable: %d", payload[idx], fieldId)
207 // TODO: Consider supporting other selector types.... Way, way in the future
208 // ok, the future is now, support strings as well... ugh.
209 var selector string
210 switch payload[idx] & 3 {
211 case 0: // Integer
212 b.SetBuf(payload[idx+1:])
213 v, e := b.DecodeVarint()
214 if e == nil {
215 log.Debugf("Decoded the ing field: %v", v)
216 selector = strconv.Itoa(int(v))
217 } else {
218 log.Errorf("Failed to decode varint %v", e)
219 return "", e
220 }
221 case 2: // Length delimited AKA string
222 b.SetBuf(payload[idx+1:])
223 v, e := b.DecodeStringBytes()
224 if e == nil {
225 log.Debugf("Decoded the string field: %v", v)
226 selector = v
227 } else {
228 log.Errorf("Failed to decode string %v", e)
229 return "", e
230 }
231 default:
232 err := errors.New(fmt.Sprintf("Only integer and string route selectors are permitted"))
233 log.Error(err)
234 return "", err
235 }
236 return selector, nil
237 } else if err := ar.skipField(&payload, &idx); err != nil {
238 log.Errorf("Parsing message failed %v", err)
239 return "", err
240 }
241 }
242}
243
244func (ar SourceRouter) Route(sel interface{}) (*backend, *connection) {
245 log.Debugf("SourceRouter sel %v", sel)
246 switch sl := sel.(type) {
247 case *requestFrame:
248 log.Debugf("Route called for nbFrame with method %s", sl.methodInfo.method)
249 // Not a south affinity binding method, proceed with north affinity binding.
250 if selector, err := ar.decodeProtoField(sl.payload, ar.methodMap[sl.methodInfo.method]); err == nil {
251 // selector is
252
253 for _, cluster := range clusters {
254 for _, backend := range cluster.backends {
255 log.Debugf("Checking backend %s", backend.name)
256 for _, connection := range backend.connections {
257 log.Debugf("Checking connection %s", connection.name)
258 // caller specified a backend and a connection
259 if backend.name+"."+connection.name == selector {
260 return backend, connection
261 }
262 }
263 // caller specified just a backend
264 if backend.name == selector {
265 return backend, nil
266 }
267 }
268 }
269 sl.err = fmt.Errorf("Backend %s not found", selector)
270 return nil, nil
271 }
272 default:
273 log.Errorf("Internal: invalid data type in Route call %v", sel)
274 return nil, nil
275 }
276 log.Errorf("Bad routing in SourceRouter:Route")
277 return nil, nil
278}
279
280func (ar SourceRouter) GetMetaKeyVal(serverStream grpc.ServerStream) (string, string, error) {
281 return "", "", nil
282}
283
284func (ar SourceRouter) IsStreaming(_ string) (bool, bool) {
285 panic("not implemented")
286}
287
288func (ar SourceRouter) BackendCluster(mthd string, metaKey string) (*cluster, error) {
289 // unsupported?
290 return ar.cluster, nil
291}
292
293func (ar SourceRouter) FindBackendCluster(beName string) *cluster {
294 // unsupported?
295 if beName == ar.cluster.name {
296 return ar.cluster
297 }
298 return nil
299}
300
301func (rr SourceRouter) ReplyHandler(sel interface{}) error { // This is a no-op
302 return nil
303}