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