blob: dcdb8d66b37774e716bacb6eeddb93833144ec96 [file] [log] [blame]
Scott Bakere7144bc2019-10-01 14:16:47 -07001/*
2 * Copyright 2019-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
19import (
20 "context"
21 "github.com/opencord/voltha-go/common/log"
22 "google.golang.org/grpc"
23 "google.golang.org/grpc/connectivity"
24 "time"
25)
26
27// connection represents a connection to a single backend
28type connection struct {
29 backend *backend
30 name string
31 addr string
32 port string
33 ctx context.Context
34 close context.CancelFunc
35}
36
37func (cn *connection) connect() {
38 for {
39 log.Infof("Connecting to %s with addr: %s and port %s", cn.name, cn.addr, cn.port)
40 // Dial doesn't block, it just returns and continues connecting in the background.
41 // Check back later to confirm and increase the connection count.
42
43 var err error
44 conn, err := grpc.Dial(cn.addr+":"+cn.port, grpc.WithCodec(Codec()), grpc.WithInsecure(), grpc.WithBackoffMaxDelay(time.Second*15))
45 if err != nil {
46 log.Fatalf("Dialing connection %v:%v", cn, err)
47 }
48
49 log.Debugf("Starting the connection monitor for '%s'", cn.name)
50 cn.monitor(conn)
51 conn.Close()
52
53 select {
54 case <-cn.ctx.Done():
55 return
56 default:
57 }
58 }
59}
60
61func (cn *connection) monitor(conn *grpc.ClientConn) {
62 be := cn.backend
63 log.Debugf("Setting up monitoring for backend %s", be.name)
64 state := connectivity.Idle
65monitorLoop:
66 for {
67 if !conn.WaitForStateChange(cn.ctx, state) {
68 log.Debugf("Context canceled for connection '%s' on backend '%s'", cn.name, be.name)
69 break monitorLoop // connection closed
70 }
71
72 if newState := conn.GetState(); newState != state {
73 previousState := state
74 state = newState
75
76 if previousState == connectivity.Ready {
77 be.decConn(cn)
78 log.Infof("Lost connection '%s' on backend '%s'", cn.name, be.name)
79 }
80
81 switch state {
82 case connectivity.Ready:
83 log.Infof("Connection '%s' on backend '%s' becomes ready", cn.name, be.name)
84 be.incConn(cn, conn)
85 case connectivity.TransientFailure, connectivity.Connecting:
86 // we don't log these, to avoid spam
87 case connectivity.Shutdown:
88 // the connection was closed
89 log.Infof("Shutdown for connection '%s' on backend '%s'", cn.name, be.name)
90 break monitorLoop
91 case connectivity.Idle:
92 // This can only happen if the server sends a GoAway. This can
93 // only happen if the server has modified MaxConnectionIdle from
94 // its default of infinity. The only solution here is to close the
95 // connection and keepTrying()?
96 //TODO: Read the grpc source code to see if there's a different approach
97 log.Errorf("Server sent 'GoAway' on connection '%s' on backend '%s'", cn.name, be.name)
98 break monitorLoop
99 }
100 }
101 }
102}