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