[VOL-785,VOL-786,VOL-1315,VOL-1316]
Initial commit of the affinity router's data plane
Change-Id: Iccc93b5526d5d2468b33eff7d8847e22fb88ef2d
diff --git a/afrouter/afrouter/method-router.go b/afrouter/afrouter/method-router.go
new file mode 100644
index 0000000..7ba175d
--- /dev/null
+++ b/afrouter/afrouter/method-router.go
@@ -0,0 +1,167 @@
+/*
+ * 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 := newRouter(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
+}