Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 1 | /* |
| 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 | */ |
Girish Gowdru | 6a80bbd | 2019-07-02 07:36:09 -0700 | [diff] [blame] | 16 | |
Scott Baker | dbd960e | 2020-02-28 08:57:51 -0800 | [diff] [blame] | 17 | //Package core provides the utility for olt devices, flows and statistics |
| 18 | package core |
Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 19 | |
| 20 | import ( |
npujar | ec5762e | 2020-01-01 14:08:48 +0530 | [diff] [blame] | 21 | "context" |
Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 22 | "reflect" |
| 23 | "runtime" |
| 24 | |
Esin Karaman | ccb714b | 2019-11-29 15:02:06 +0000 | [diff] [blame] | 25 | "github.com/opencord/voltha-lib-go/v3/pkg/log" |
Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 26 | ) |
| 27 | |
| 28 | // DeviceState OLT Device state |
| 29 | type DeviceState int |
| 30 | |
| 31 | const ( |
| 32 | // deviceStateNull OLT is not instantiated |
| 33 | deviceStateNull DeviceState = iota |
| 34 | // deviceStateInit OLT is instantiated |
| 35 | deviceStateInit |
| 36 | // deviceStateConnected Grpc session established with OLT |
| 37 | deviceStateConnected |
| 38 | // deviceStateUp Admin state of OLT is UP |
| 39 | deviceStateUp |
| 40 | // deviceStateDown Admin state of OLT is down |
| 41 | deviceStateDown |
| 42 | ) |
| 43 | |
| 44 | // Trigger for changing the state |
| 45 | type Trigger int |
| 46 | |
| 47 | const ( |
| 48 | // DeviceInit Go to Device init state |
| 49 | DeviceInit Trigger = iota |
| 50 | // GrpcConnected Go to connected state |
| 51 | GrpcConnected |
| 52 | // DeviceUpInd Go to Device up state |
| 53 | DeviceUpInd |
| 54 | // DeviceDownInd Go to Device down state |
| 55 | DeviceDownInd |
| 56 | // GrpcDisconnected Go to Device init state |
| 57 | GrpcDisconnected |
| 58 | ) |
| 59 | |
| 60 | // TransitionHandler function type for handling transition |
npujar | ec5762e | 2020-01-01 14:08:48 +0530 | [diff] [blame] | 61 | type TransitionHandler func(ctx context.Context) error |
Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 62 | |
| 63 | // Transition to store state machine |
| 64 | type Transition struct { |
| 65 | previousState []DeviceState |
| 66 | currentState DeviceState |
| 67 | before []TransitionHandler |
| 68 | after []TransitionHandler |
| 69 | } |
| 70 | |
| 71 | // TransitionMap to store all the states and current device state |
| 72 | type TransitionMap struct { |
| 73 | transitions map[Trigger]Transition |
| 74 | currentDeviceState DeviceState |
| 75 | } |
| 76 | |
| 77 | // OpenoltDevice state machine: |
| 78 | // |
| 79 | // null ----> init ------> connected -----> up -----> down |
| 80 | // ^ ^ | ^ | | |
| 81 | // | | | | | | |
| 82 | // | +-------------+ +---------+ | |
| 83 | // | | |
| 84 | // +-----------------------------------------+ |
| 85 | |
| 86 | // NewTransitionMap create a new state machine with all the transitions |
| 87 | func NewTransitionMap(dh *DeviceHandler) *TransitionMap { |
| 88 | var transitionMap TransitionMap |
| 89 | transitionMap.currentDeviceState = deviceStateNull |
| 90 | transitionMap.transitions = make(map[Trigger]Transition) |
| 91 | // In doInit establish the grpc session |
| 92 | transitionMap.transitions[DeviceInit] = |
| 93 | Transition{ |
Chaitrashree G S | a464925 | 2020-03-11 21:24:11 -0400 | [diff] [blame] | 94 | previousState: []DeviceState{deviceStateNull, deviceStateUp, deviceStateDown}, |
Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 95 | currentState: deviceStateInit, |
| 96 | before: []TransitionHandler{dh.doStateInit}, |
| 97 | after: []TransitionHandler{dh.postInit}} |
| 98 | // If gRpc session fails, re-establish the grpc session |
| 99 | transitionMap.transitions[GrpcDisconnected] = |
| 100 | Transition{ |
| 101 | previousState: []DeviceState{deviceStateConnected, deviceStateDown}, |
| 102 | currentState: deviceStateInit, |
| 103 | before: []TransitionHandler{dh.doStateInit}, |
| 104 | after: []TransitionHandler{dh.postInit}} |
| 105 | // in doConnected, create logical device and read the indications |
| 106 | transitionMap.transitions[GrpcConnected] = |
| 107 | Transition{ |
| 108 | previousState: []DeviceState{deviceStateInit}, |
| 109 | currentState: deviceStateConnected, |
| 110 | before: []TransitionHandler{dh.doStateConnected}} |
| 111 | |
| 112 | // Once the olt UP is indication received, then do state up |
| 113 | transitionMap.transitions[DeviceUpInd] = |
| 114 | Transition{ |
| 115 | previousState: []DeviceState{deviceStateConnected, deviceStateDown}, |
| 116 | currentState: deviceStateUp, |
| 117 | before: []TransitionHandler{dh.doStateUp}} |
| 118 | // If olt DOWN indication comes then do sate down |
| 119 | transitionMap.transitions[DeviceDownInd] = |
| 120 | Transition{ |
| 121 | previousState: []DeviceState{deviceStateUp}, |
| 122 | currentState: deviceStateDown, |
| 123 | before: []TransitionHandler{dh.doStateDown}} |
| 124 | |
| 125 | return &transitionMap |
| 126 | } |
| 127 | |
| 128 | // funcName gets the handler function name |
| 129 | func funcName(f interface{}) string { |
| 130 | p := reflect.ValueOf(f).Pointer() |
| 131 | rf := runtime.FuncForPC(p) |
| 132 | return rf.Name() |
| 133 | } |
| 134 | |
| 135 | // isValidTransition checks for the new state transition is valid from current state |
| 136 | func (tMap *TransitionMap) isValidTransition(trigger Trigger) bool { |
| 137 | // Validate the state transition |
| 138 | for _, state := range tMap.transitions[trigger].previousState { |
| 139 | if tMap.currentDeviceState == state { |
| 140 | return true |
| 141 | } |
| 142 | } |
| 143 | return false |
| 144 | } |
| 145 | |
| 146 | // Handle moves the state machine to next state based on the trigger and invokes the before and |
Girish Gowdru | 6a80bbd | 2019-07-02 07:36:09 -0700 | [diff] [blame] | 147 | // after handlers if the transition is a valid transition |
npujar | ec5762e | 2020-01-01 14:08:48 +0530 | [diff] [blame] | 148 | func (tMap *TransitionMap) Handle(ctx context.Context, trigger Trigger) { |
Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 149 | |
| 150 | // Check whether the transtion is valid from current state |
| 151 | if !tMap.isValidTransition(trigger) { |
Neha Sharma | 96b7bf2 | 2020-06-15 10:37:32 +0000 | [diff] [blame] | 152 | logger.Errorw(ctx, "invalid-transition-triggered", |
Shrey Baid | 2691297 | 2020-04-16 21:02:31 +0530 | [diff] [blame] | 153 | log.Fields{ |
| 154 | "current-state": tMap.currentDeviceState, |
| 155 | "trigger": trigger}) |
Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 156 | return |
| 157 | } |
| 158 | |
| 159 | // Invoke the before handlers |
| 160 | beforeHandlers := tMap.transitions[trigger].before |
Girish Gowdru | 6a80bbd | 2019-07-02 07:36:09 -0700 | [diff] [blame] | 161 | if beforeHandlers == nil { |
Neha Sharma | 96b7bf2 | 2020-06-15 10:37:32 +0000 | [diff] [blame] | 162 | logger.Debugw(ctx, "no-handlers-for-before", log.Fields{"trigger": trigger}) |
Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 163 | } |
Girish Gowdru | 6a80bbd | 2019-07-02 07:36:09 -0700 | [diff] [blame] | 164 | for _, handler := range beforeHandlers { |
Neha Sharma | 96b7bf2 | 2020-06-15 10:37:32 +0000 | [diff] [blame] | 165 | logger.Debugw(ctx, "running-before-handler", log.Fields{"handler": funcName(handler)}) |
npujar | ec5762e | 2020-01-01 14:08:48 +0530 | [diff] [blame] | 166 | if err := handler(ctx); err != nil { |
Girish Gowdru | 6a80bbd | 2019-07-02 07:36:09 -0700 | [diff] [blame] | 167 | // TODO handle error |
Neha Sharma | 96b7bf2 | 2020-06-15 10:37:32 +0000 | [diff] [blame] | 168 | logger.Error(ctx, err) |
Girish Gowdru | 6a80bbd | 2019-07-02 07:36:09 -0700 | [diff] [blame] | 169 | return |
| 170 | } |
| 171 | } |
Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 172 | |
| 173 | // Update the state |
| 174 | tMap.currentDeviceState = tMap.transitions[trigger].currentState |
Neha Sharma | 96b7bf2 | 2020-06-15 10:37:32 +0000 | [diff] [blame] | 175 | logger.Debugw(ctx, "updated-device-state ", log.Fields{"current-device-state": tMap.currentDeviceState}) |
Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 176 | |
| 177 | // Invoke the after handlers |
| 178 | afterHandlers := tMap.transitions[trigger].after |
Girish Gowdru | 6a80bbd | 2019-07-02 07:36:09 -0700 | [diff] [blame] | 179 | if afterHandlers == nil { |
Neha Sharma | 96b7bf2 | 2020-06-15 10:37:32 +0000 | [diff] [blame] | 180 | logger.Debugw(ctx, "no-handlers-for-after", log.Fields{"trigger": trigger}) |
Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 181 | } |
Girish Gowdru | 6a80bbd | 2019-07-02 07:36:09 -0700 | [diff] [blame] | 182 | for _, handler := range afterHandlers { |
Neha Sharma | 96b7bf2 | 2020-06-15 10:37:32 +0000 | [diff] [blame] | 183 | logger.Debugw(ctx, "running-after-handler", log.Fields{"handler": funcName(handler)}) |
npujar | ec5762e | 2020-01-01 14:08:48 +0530 | [diff] [blame] | 184 | if err := handler(ctx); err != nil { |
Girish Gowdru | 6a80bbd | 2019-07-02 07:36:09 -0700 | [diff] [blame] | 185 | // TODO handle error |
Neha Sharma | 96b7bf2 | 2020-06-15 10:37:32 +0000 | [diff] [blame] | 186 | logger.Error(ctx, err) |
Girish Gowdru | 6a80bbd | 2019-07-02 07:36:09 -0700 | [diff] [blame] | 187 | return |
| 188 | } |
| 189 | } |
Girish Gowdru | 0c588b2 | 2019-04-23 23:24:56 -0400 | [diff] [blame] | 190 | } |