blob: e4824daec33e9a1ff5f39bf8644b578e7bc9972b [file] [log] [blame]
/*
* Copyright 2018-present Open Networking Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// gRPC affinity router with active/active backends
package afrouter
import (
"fmt"
"errors"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"github.com/opencord/voltha-go/common/log"
)
const NoMeta = "nometa"
type MethodRouter struct {
name string
service string
mthdRt map[string]map[string]Router // map of [metadata][method]
}
func newMethodRouter(config *RouterConfig) (Router, error) {
mr := MethodRouter{name:config.Name,service:config.ProtoService,mthdRt:make(map[string]map[string]Router)}
mr.mthdRt[NoMeta] = make(map[string]Router) // For routes not needing metadata (all expcept binding at this time)
if len(config.Routes) == 0 {
return nil, errors.New(fmt.Sprintf("Router %s must have at least one route", config.Name))
}
for _,rtv := range config.Routes {
var idx1 string
r,err := newSubRouter(config, &rtv)
if err != nil {
return nil, err
}
if rtv.Type == "binding" {
idx1 = rtv.Binding.Field
if _,ok := mr.mthdRt[idx1]; ok == false { // /First attempt on this key
mr.mthdRt[idx1] = make(map[string]Router)
}
} else {
idx1 = NoMeta
}
switch len(rtv.Methods) {
case 0:
return nil, errors.New(fmt.Sprintf("Route for router %s must have at least one method", config.Name))
case 1:
if rtv.Methods[0] == "*" {
return r, nil
} else {
log.Debugf("Setting router '%s' for single method '%s'",r.Name(),rtv.Methods[0])
if _,ok := mr.mthdRt[""][rtv.Methods[0]]; ok == false {
mr.mthdRt[idx1][rtv.Methods[0]] = r
} else {
err := errors.New(fmt.Sprintf("Attempt to define method %s for 2 routes: %s & %s", rtv.Methods[0],
r.Name(), mr.mthdRt[idx1][rtv.Methods[0]].Name()))
log.Debug(err)
return mr, err
}
}
default:
for _,m := range rtv.Methods {
log.Debugf("Setting router '%s' for method '%s'",r.Name(),m)
if _,ok := mr.mthdRt[idx1][m]; ok == false {
mr.mthdRt[idx1][m] = r
} else {
err := errors.New(fmt.Sprintf("Attempt to define method %s for 2 routes: %s & %s", rtv.Methods[0],
r.Name(), mr.mthdRt[idx1][m].Name()))
log.Debug(err)
return mr, err
}
}
}
}
return mr, nil
}
func (mr MethodRouter) Name() string {
return mr.name
}
func (mr MethodRouter) Service() string {
return mr.service
}
func (mr MethodRouter) GetMetaKeyVal(serverStream grpc.ServerStream) (string,string,error) {
var rtrnK string = NoMeta
var rtrnV string = ""
// Get the metadata from the server stream
md, ok := metadata.FromIncomingContext(serverStream.Context())
if !ok {
return rtrnK, rtrnV, errors.New("Could not get a server stream metadata")
}
// Determine if one of the method routing keys exists in the metadata
for k,_ := range mr.mthdRt {
if _,ok := md[k]; ok == true {
rtrnV = md[k][0]
rtrnK = k
break
}
}
return rtrnK,rtrnV,nil
}
func (mr MethodRouter) ReplyHandler(sel interface{}) error {
switch sl := sel.(type) {
case *sbFrame:
if r,ok := mr.mthdRt[NoMeta][sl.method]; ok == true {
return r.ReplyHandler(sel)
}
// TODO: this case should also be an error
default: //TODO: This should really be a big error
// A reply handler should only be called on the sbFrame
return nil
}
return nil
}
func (mr MethodRouter) Route(sel interface{}) *backend {
switch sl := sel.(type) {
case *nbFrame:
if r,ok := mr.mthdRt[sl.metaKey][sl.mthdSlice[REQ_METHOD]]; ok == true {
return r.Route(sel)
}
log.Errorf("Attept to route on non-existent method '%s'", sl.mthdSlice[REQ_METHOD])
return nil
default:
return nil
}
return nil
}
func (mr MethodRouter) BackendCluster(mthd string, metaKey string) (*backendCluster,error) {
if r,ok := mr.mthdRt[metaKey][mthd]; ok == true {
return r.BackendCluster(mthd, metaKey)
}
err := errors.New(fmt.Sprintf("No backend cluster exists for method '%s' using meta key '%s'", mthd,metaKey))
log.Error(err)
return nil, err
}
func (mr MethodRouter) FindBackendCluster(beName string) *backendCluster {
for _,meta := range mr.mthdRt {
for _,r := range meta {
if rtrn := r.FindBackendCluster(beName); rtrn != nil {
return rtrn
}
}
}
return nil
}