blob: 51b7ef0e1a9374912f6adb9ac19e2f032ebe23e2 [file] [log] [blame]
Girish Gowdru0c588b22019-04-23 23:24:56 -04001/*
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 Gowdru6a80bbd2019-07-02 07:36:09 -070016
17//Package adaptercore provides the utility for olt devices, flows and statistics
Girish Gowdru0c588b22019-04-23 23:24:56 -040018package adaptercore
19
20import (
npujarec5762e2020-01-01 14:08:48 +053021 "context"
Girish Gowdru0c588b22019-04-23 23:24:56 -040022 "reflect"
23 "runtime"
24
Esin Karamanccb714b2019-11-29 15:02:06 +000025 "github.com/opencord/voltha-lib-go/v3/pkg/log"
Girish Gowdru0c588b22019-04-23 23:24:56 -040026)
27
28// DeviceState OLT Device state
29type DeviceState int
30
31const (
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
45type Trigger int
46
47const (
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
npujarec5762e2020-01-01 14:08:48 +053061type TransitionHandler func(ctx context.Context) error
Girish Gowdru0c588b22019-04-23 23:24:56 -040062
63// Transition to store state machine
64type 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
72type 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
87func 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{
Girish Gowdrud4245152019-05-10 00:47:31 -040094 previousState: []DeviceState{deviceStateNull, deviceStateDown},
Girish Gowdru0c588b22019-04-23 23:24:56 -040095 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
129func 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
136func (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 Gowdru6a80bbd2019-07-02 07:36:09 -0700147// after handlers if the transition is a valid transition
npujarec5762e2020-01-01 14:08:48 +0530148func (tMap *TransitionMap) Handle(ctx context.Context, trigger Trigger) {
Girish Gowdru0c588b22019-04-23 23:24:56 -0400149
150 // Check whether the transtion is valid from current state
151 if !tMap.isValidTransition(trigger) {
152 log.Errorw("Invalid transition triggered ", log.Fields{"CurrentState": tMap.currentDeviceState, "Trigger": trigger})
153 return
154 }
155
156 // Invoke the before handlers
157 beforeHandlers := tMap.transitions[trigger].before
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700158 if beforeHandlers == nil {
Girish Gowdru0c588b22019-04-23 23:24:56 -0400159 log.Debugw("No handlers for before", log.Fields{"trigger": trigger})
160 }
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700161 for _, handler := range beforeHandlers {
162 log.Debugw("running-before-handler", log.Fields{"handler": funcName(handler)})
npujarec5762e2020-01-01 14:08:48 +0530163 if err := handler(ctx); err != nil {
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700164 // TODO handle error
165 log.Error(err)
166 return
167 }
168 }
Girish Gowdru0c588b22019-04-23 23:24:56 -0400169
170 // Update the state
171 tMap.currentDeviceState = tMap.transitions[trigger].currentState
172 log.Debugw("Updated device state ", log.Fields{"CurrentDeviceState": tMap.currentDeviceState})
173
174 // Invoke the after handlers
175 afterHandlers := tMap.transitions[trigger].after
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700176 if afterHandlers == nil {
Girish Gowdru0c588b22019-04-23 23:24:56 -0400177 log.Debugw("No handlers for after", log.Fields{"trigger": trigger})
178 }
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700179 for _, handler := range afterHandlers {
180 log.Debugw("running-after-handler", log.Fields{"handler": funcName(handler)})
npujarec5762e2020-01-01 14:08:48 +0530181 if err := handler(ctx); err != nil {
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700182 // TODO handle error
183 log.Error(err)
184 return
185 }
186 }
Girish Gowdru0c588b22019-04-23 23:24:56 -0400187}