| /* |
| * Copyright 2018-2023 Open Networking Foundation (ONF) and the ONF Contributors |
| |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package state |
| |
| import ( |
| "context" |
| "reflect" |
| "runtime" |
| |
| "github.com/opencord/voltha-lib-go/v7/pkg/log" |
| "github.com/opencord/voltha-protos/v5/go/core" |
| "github.com/opencord/voltha-protos/v5/go/voltha" |
| ) |
| |
| // deviceType mentions type of device like parent, child |
| type deviceType int32 |
| |
| const ( |
| parent deviceType = 0 |
| child deviceType = 1 |
| any deviceType = 2 |
| ) |
| |
| type matchResult uint16 |
| |
| const ( |
| noMatch matchResult = iota // current state has not match in the transition table |
| currWildcardMatch // current state matches the wildcard *_UNKNOWN state in the transition table |
| currStateOnlyMatch // current state matches the current state and previous state matches the wildcard in the transition table |
| currPrevStateMatch // both current and previous states match in the transition table |
| ) |
| |
| // match is used to keep the current match states |
| type match struct { |
| admin, oper, conn, transient matchResult |
| } |
| |
| // toInt returns an integer representing the matching level of the match (the larger the number the better) |
| func (m *match) toInt() int { |
| return int(m.transient<<8 | m.admin<<4 | m.oper<<2 | m.conn) |
| } |
| |
| // isExactMatch returns true if match is an exact match |
| func (m *match) isExactMatch() bool { |
| return m.admin == currPrevStateMatch && m.oper == currPrevStateMatch && m.conn == currPrevStateMatch && |
| m.transient == currPrevStateMatch |
| } |
| |
| // isBetterMatch returns true if newMatch is a worse match |
| func (m *match) isBetterMatch(newMatch *match) bool { |
| return m.toInt() > newMatch.toInt() |
| } |
| |
| // deviceState has admin, operational and connection status of device |
| type deviceState struct { |
| Admin voltha.AdminState_Types |
| Connection voltha.ConnectStatus_Types |
| Operational voltha.OperStatus_Types |
| Transient core.DeviceTransientState_Types |
| } |
| |
| // transitionHandler function type which takes the current and previous device info as input parameter |
| type transitionHandler func(context.Context, *voltha.Device) error |
| |
| // transition represent transition related attributes |
| type transition struct { |
| deviceType deviceType |
| previousState deviceState |
| currentState deviceState |
| handlers []transitionHandler |
| } |
| |
| // TransitionMap represent map of transitions and device manager |
| type TransitionMap struct { |
| transitions []transition |
| dMgr DeviceManager |
| } |
| |
| // DeviceManager represents a generic device manager |
| type DeviceManager interface { |
| NotifyInvalidTransition(ctx context.Context, curr *voltha.Device) error |
| CreateLogicalDevice(ctx context.Context, curr *voltha.Device) error |
| SetupUNILogicalPorts(ctx context.Context, curr *voltha.Device) error |
| DeleteLogicalDevice(ctx context.Context, curr *voltha.Device) error |
| DeleteLogicalPorts(ctx context.Context, curr *voltha.Device) error |
| DeleteAllChildDevices(ctx context.Context, curr *voltha.Device) error |
| RunPostDeviceDelete(ctx context.Context, curr *voltha.Device) error |
| ChildDeviceLost(ctx context.Context, curr *voltha.Device) error |
| DeleteAllLogicalPorts(ctx context.Context, curr *voltha.Device) error |
| DeleteAllLogicalMeters(ctx context.Context, curr *voltha.Device) error |
| DeleteAllDeviceFlows(ctx context.Context, curr *voltha.Device) error |
| ReconcilingCleanup(ctx context.Context, curr *voltha.Device) error |
| } |
| |
| // NewTransitionMap creates transition map |
| func NewTransitionMap(dMgr DeviceManager) *TransitionMap { |
| var transitionMap TransitionMap |
| transitionMap.dMgr = dMgr |
| transitionMap.transitions = make([]transition, 0) |
| transitionMap.transitions = append( |
| transitionMap.transitions, |
| transition{ |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVE, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.CreateLogicalDevice}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: child, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_DISCOVERED, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: child, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_DISCOVERED, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVE, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.SetupUNILogicalPorts}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: child, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_DISCOVERED, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: child, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVE, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.SetupUNILogicalPorts}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ //DELETE PRE PROVISIONED State device forcefully |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_ANY}, |
| currentState: deviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_FORCE_DELETING}, |
| handlers: []transitionHandler{dMgr.RunPostDeviceDelete}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ // DELETE PRE PROVISIONED State device no force option set |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_ANY}, |
| currentState: deviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_DELETING_POST_ADAPTER_RESPONSE}, |
| handlers: []transitionHandler{dMgr.RunPostDeviceDelete}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ //DELETE device forcefully |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_ANY}, |
| currentState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_FORCE_DELETING}, |
| handlers: []transitionHandler{dMgr.DeleteAllLogicalPorts, dMgr.DeleteAllChildDevices, dMgr.DeleteAllLogicalMeters, dMgr.RunPostDeviceDelete}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ //DELETE device after adapter response |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_ANY}, |
| currentState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_DELETING_POST_ADAPTER_RESPONSE}, |
| handlers: []transitionHandler{dMgr.DeleteAllLogicalPorts, dMgr.DeleteAllChildDevices, dMgr.DeleteAllLogicalMeters, dMgr.RunPostDeviceDelete}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ //DELETE no operation transition |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_ANY}, |
| currentState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_DELETING_FROM_ADAPTER}, |
| handlers: []transitionHandler{}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_ACTIVE, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNREACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNREACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_REBOOTED, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.DeleteAllLogicalPorts, dMgr.DeleteAllChildDevices, dMgr.DeleteAllLogicalMeters, dMgr.DeleteLogicalDevice, dMgr.DeleteAllDeviceFlows}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNREACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_REBOOTED, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.DeleteAllLogicalPorts, dMgr.DeleteAllChildDevices, dMgr.DeleteAllLogicalMeters, dMgr.DeleteLogicalDevice, dMgr.DeleteAllDeviceFlows}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNREACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNREACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_ACTIVE, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.CreateLogicalDevice}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNREACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.CreateLogicalDevice}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ //DELETE force case |
| deviceType: child, |
| previousState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_ANY}, |
| currentState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_FORCE_DELETING}, |
| handlers: []transitionHandler{dMgr.DeleteAllDeviceFlows, dMgr.ChildDeviceLost, dMgr.DeleteLogicalPorts, dMgr.RunPostDeviceDelete}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ //DELETE after adapter response case |
| deviceType: child, |
| previousState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_ANY}, |
| currentState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_DELETING_POST_ADAPTER_RESPONSE}, |
| handlers: []transitionHandler{dMgr.DeleteAllDeviceFlows, dMgr.ChildDeviceLost, dMgr.DeleteLogicalPorts, dMgr.RunPostDeviceDelete}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ //DELETE wait for adapter response(no operation) |
| deviceType: child, |
| previousState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_ANY}, |
| currentState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_DELETING_FROM_ADAPTER}, |
| handlers: []transitionHandler{}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVE, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.NotifyInvalidTransition}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.NotifyInvalidTransition}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.NotifyInvalidTransition}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: child, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_DOWNLOADING_IMAGE, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: child, |
| previousState: deviceState{Admin: voltha.AdminState_DOWNLOADING_IMAGE, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.NotifyInvalidTransition}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVE, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.NotifyInvalidTransition}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.NotifyInvalidTransition}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.NotifyInvalidTransition}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: child, |
| previousState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.NotifyInvalidTransition}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| currentState: deviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_NONE}, |
| handlers: []transitionHandler{dMgr.NotifyInvalidTransition}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_DOWNLOADING_IMAGE, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_DOWNLOADING_IMAGE, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_ACTIVE, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{dMgr.ReconcilingCleanup}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNREACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{dMgr.ReconcilingCleanup}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNREACHABLE, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{dMgr.ReconcilingCleanup}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_DOWNLOADING_IMAGE, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_DOWNLOADING_IMAGE, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{dMgr.ReconcilingCleanup}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING_FAILED, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{dMgr.ReconcilingCleanup}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING_FAILED, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{dMgr.ReconcilingCleanup}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: any, |
| previousState: deviceState{Admin: voltha.AdminState_DOWNLOADING_IMAGE, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_DOWNLOADING_IMAGE, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_RECONCILING_FAILED, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{dMgr.ReconcilingCleanup}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_REBOOTED, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{dMgr.DeleteAllLogicalPorts, dMgr.DeleteAllChildDevices, dMgr.DeleteAllLogicalMeters, dMgr.DeleteLogicalDevice, dMgr.DeleteAllDeviceFlows}}) |
| transitionMap.transitions = append(transitionMap.transitions, |
| transition{ |
| deviceType: parent, |
| previousState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNREACHABLE, Operational: voltha.OperStatus_RECONCILING, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| currentState: deviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_REBOOTED, Transient: core.DeviceTransientState_RECONCILE_IN_PROGRESS}, |
| handlers: []transitionHandler{dMgr.DeleteAllLogicalPorts, dMgr.DeleteAllChildDevices, dMgr.DeleteAllLogicalMeters, dMgr.DeleteLogicalDevice, dMgr.DeleteAllDeviceFlows}}) |
| |
| return &transitionMap |
| } |
| |
| func getDeviceStates(device *voltha.Device, transientState core.DeviceTransientState_Types) deviceState { |
| return deviceState{Admin: device.AdminState, Connection: device.ConnectStatus, Operational: device.OperStatus, |
| Transient: transientState} |
| } |
| |
| // isMatched matches a state transition. It returns whether there is a match and if there is whether it is an exact match |
| func getHandler(previous deviceState, current deviceState, transition *transition) ([]transitionHandler, *match) { |
| m := &match{} |
| var waitForOtherStatesMatch bool |
| // Do we have an exact match? |
| if previous == transition.previousState && current == transition.currentState { |
| return transition.handlers, &match{admin: currPrevStateMatch, oper: currPrevStateMatch, conn: currPrevStateMatch, |
| transient: currPrevStateMatch} |
| } |
| // Do we have transient state match? |
| if current.Transient == transition.currentState.Transient && transition.currentState.Transient != core.DeviceTransientState_ANY { |
| if previous.Transient == transition.previousState.Transient || transition.previousState.Transient == core.DeviceTransientState_ANY { |
| m.transient = currPrevStateMatch |
| } else { |
| m.transient = currStateOnlyMatch |
| } |
| } else if current.Transient == transition.currentState.Transient && transition.currentState.Transient == core.DeviceTransientState_ANY { |
| if previous.Transient == transition.previousState.Transient || transition.previousState.Transient == core.DeviceTransientState_ANY { |
| m.transient = currWildcardMatch |
| } |
| } |
| if m.transient == noMatch { |
| return nil, m |
| } |
| |
| // Do we have Admin state match? |
| if current.Admin == transition.currentState.Admin && transition.currentState.Admin != voltha.AdminState_UNKNOWN { |
| if previous.Admin == transition.previousState.Admin { |
| m.admin = currPrevStateMatch |
| } else if transition.previousState.Admin == voltha.AdminState_UNKNOWN { |
| m.admin = currStateOnlyMatch |
| } |
| } else if current.Admin == transition.currentState.Admin && transition.currentState.Admin == voltha.AdminState_UNKNOWN { |
| if previous.Admin == transition.previousState.Admin || transition.previousState.Admin == voltha.AdminState_UNKNOWN { |
| m.admin = currWildcardMatch |
| } |
| } else if transition.previousState.Admin == voltha.AdminState_UNKNOWN && transition.currentState.Admin == voltha.AdminState_UNKNOWN { |
| // Will only be the case of DELETION currently.(to allow only transient match, we can avoid this check if |
| // we can allow wild card in admin state) |
| waitForOtherStatesMatch = true |
| } |
| if !waitForOtherStatesMatch && m.admin == noMatch { |
| return nil, m |
| } |
| // Do we have an operational state match? |
| if current.Operational == transition.currentState.Operational && transition.previousState.Operational != voltha.OperStatus_UNKNOWN { |
| if previous.Operational == transition.previousState.Operational || transition.previousState.Operational == voltha.OperStatus_UNKNOWN { |
| m.oper = currPrevStateMatch |
| } else { |
| m.oper = currStateOnlyMatch |
| } |
| } else if current.Operational == transition.currentState.Operational && transition.previousState.Operational == voltha.OperStatus_UNKNOWN { |
| if previous.Operational == transition.previousState.Operational || transition.previousState.Operational == voltha.OperStatus_UNKNOWN { |
| m.oper = currWildcardMatch |
| } |
| } |
| |
| // Do we have an connection state match? |
| if current.Connection == transition.currentState.Connection && transition.previousState.Connection != voltha.ConnectStatus_UNKNOWN { |
| if previous.Connection == transition.previousState.Connection || transition.previousState.Connection == voltha.ConnectStatus_UNKNOWN { |
| m.conn = currPrevStateMatch |
| } else { |
| m.conn = currStateOnlyMatch |
| } |
| } else if current.Connection == transition.currentState.Connection && transition.previousState.Connection == voltha.ConnectStatus_UNKNOWN { |
| if previous.Connection == transition.previousState.Connection || transition.previousState.Connection == voltha.ConnectStatus_UNKNOWN { |
| m.conn = currWildcardMatch |
| } |
| } |
| return transition.handlers, m |
| } |
| |
| // getTransitionHandler returns transition handler & a flag that's set if the transition is invalid |
| func (tMap *TransitionMap) getTransitionHandler(ctx context.Context, cDevice, pDevice *voltha.Device, |
| cTransientState, pTransientState core.DeviceTransientState_Types) []transitionHandler { |
| //1. Get the previous and current set of states |
| cState := getDeviceStates(cDevice, cTransientState) |
| pState := getDeviceStates(pDevice, pTransientState) |
| |
| // Do nothing is there are no states change |
| if pState == cState { |
| return nil |
| } |
| |
| //logger.Infow(ctx, "deviceType", log.Fields{"device": pDevice}) |
| deviceType := parent |
| if !cDevice.Root { |
| logger.Info(ctx, "device is child") |
| deviceType = child |
| } |
| logger.Infof(ctx, "deviceType:%d-deviceId:%s-previous:%v-current:%v", deviceType, cDevice.Id, pState, cState) |
| |
| //2. Go over transition array to get the right transition |
| var currentMatch []transitionHandler |
| var tempHandler []transitionHandler |
| var m *match |
| bestMatch := &match{} |
| for i := range tMap.transitions { |
| // consider transition only if it matches deviceType or is a wild card - any |
| if tMap.transitions[i].deviceType != deviceType && tMap.transitions[i].deviceType != any { |
| continue |
| } |
| tempHandler, m = getHandler(pState, cState, &tMap.transitions[i]) |
| if tempHandler != nil { |
| if m.isExactMatch() && tMap.transitions[i].deviceType == deviceType { |
| return tempHandler |
| } else if m.isExactMatch() || m.isBetterMatch(bestMatch) { |
| currentMatch = tempHandler |
| bestMatch = m |
| } |
| } |
| } |
| return currentMatch |
| } |
| |
| func (tMap *TransitionMap) ProcessTransition(ctx context.Context, device, prevDevice *voltha.Device, |
| deviceTransientState, prevDeviceTransientState core.DeviceTransientState_Types) error { |
| // This will be triggered on every state update |
| logger.Debugw(ctx, "state-transition", log.Fields{ |
| "device": device.Id, |
| "prev-admin-state": prevDevice.AdminState, |
| "prev-oper-state": prevDevice.OperStatus, |
| "prev-conn-state": prevDevice.ConnectStatus, |
| "curr-admin-state": device.AdminState, |
| "curr-oper-state": device.OperStatus, |
| "curr-conn-state": device.ConnectStatus, |
| "curr-transient-state": deviceTransientState, |
| "prev-transient-state": prevDeviceTransientState, |
| }) |
| handlers := tMap.getTransitionHandler(ctx, device, prevDevice, deviceTransientState, prevDeviceTransientState) |
| if len(handlers) == 0 { |
| logger.Debugw(ctx, "no-op-transition", log.Fields{"deviceId": device.Id}) |
| return nil |
| } |
| logger.Debugw(ctx, "handler-found", log.Fields{"num-expectedHandlers": len(handlers), "isParent": device.Root, |
| "current-data": device, "previous-data": prevDevice}) |
| for _, handler := range handlers { |
| logger.Debugw(ctx, "running-handler", log.Fields{"handler": funcName(handler)}) |
| if err := handler(ctx, device); err != nil { |
| logger.Warnw(ctx, "handler-failed", log.Fields{"handler": funcName(handler), "error": err}) |
| return err |
| } |
| } |
| return nil |
| } |
| |
| func funcName(f interface{}) string { |
| return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() |
| } |