blob: ad7b523e959bedaa337382f2bdadf094c7ab1d8d [file] [log] [blame]
khenaidoob9203542018-09-17 22:56:37 -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 */
npujar1d86a522019-11-14 17:11:16 +053016
Kent Hagerman2b216042020-04-03 18:28:56 -040017package device
khenaidoob9203542018-09-17 22:56:37 -040018
19import (
20 "context"
Matteo Scandolo360605d2019-11-05 18:29:17 -080021 "encoding/hex"
Kent Hagerman4f355f52020-03-30 16:01:33 -040022 "errors"
khenaidoo3ab34882019-05-02 21:33:30 -040023 "fmt"
Mahir Gunyelb0343bf2021-05-11 14:14:26 -070024 "reflect"
25 "sync"
26 "time"
27
khenaidoo9beaaf12021-10-19 17:32:01 -040028 "github.com/opencord/voltha-protos/v5/go/adapter_service"
khenaidood948f772021-08-11 17:49:24 -040029 "github.com/opencord/voltha-protos/v5/go/core"
khenaidoo9beaaf12021-10-19 17:32:01 -040030 "github.com/opencord/voltha-protos/v5/go/omci"
khenaidood948f772021-08-11 17:49:24 -040031
Maninder0aabf0c2021-03-17 14:55:14 +053032 "github.com/cenkalti/backoff/v3"
Maninder9a1bc0d2020-10-26 11:34:02 +053033 "github.com/gogo/protobuf/proto"
Maninder9a1bc0d2020-10-26 11:34:02 +053034 "github.com/golang/protobuf/ptypes/empty"
Maninder0aabf0c2021-03-17 14:55:14 +053035 "github.com/opencord/voltha-go/rw_core/config"
Maninder9a1bc0d2020-10-26 11:34:02 +053036 "google.golang.org/grpc/codes"
37 "google.golang.org/grpc/status"
Mahir Gunyeladdb66a2020-04-29 18:08:50 -070038
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053039 "github.com/opencord/voltha-go/db/model"
Kent Hagerman2b216042020-04-03 18:28:56 -040040 "github.com/opencord/voltha-go/rw_core/core/adapter"
Mahir Gunyel03de0d32020-06-03 01:36:59 -070041 "github.com/opencord/voltha-go/rw_core/core/device/flow"
42 "github.com/opencord/voltha-go/rw_core/core/device/group"
Kent Hagerman2a07b862020-06-19 15:23:07 -040043 "github.com/opencord/voltha-go/rw_core/core/device/port"
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053044 "github.com/opencord/voltha-go/rw_core/core/device/transientstate"
Scott Bakerb671a862019-10-24 10:53:40 -070045 coreutils "github.com/opencord/voltha-go/rw_core/utils"
khenaidood948f772021-08-11 17:49:24 -040046 "github.com/opencord/voltha-lib-go/v7/pkg/log"
47 "github.com/opencord/voltha-protos/v5/go/common"
khenaidoo9beaaf12021-10-19 17:32:01 -040048 ca "github.com/opencord/voltha-protos/v5/go/core_adapter"
khenaidood948f772021-08-11 17:49:24 -040049 "github.com/opencord/voltha-protos/v5/go/extension"
khenaidood948f772021-08-11 17:49:24 -040050 ofp "github.com/opencord/voltha-protos/v5/go/openflow_13"
51 "github.com/opencord/voltha-protos/v5/go/voltha"
khenaidoob9203542018-09-17 22:56:37 -040052)
53
khenaidood948f772021-08-11 17:49:24 -040054var errReconcileAborted = errors.New("reconcile aborted")
55var errContextExpired = errors.New("context expired")
56
Kent Hagerman2b216042020-04-03 18:28:56 -040057// Agent represents device agent attributes
58type Agent struct {
Maninder0aabf0c2021-03-17 14:55:14 +053059 deviceID string
60 parentID string
61 deviceType string
khenaidood948f772021-08-11 17:49:24 -040062 adapterEndpoint string
Maninder0aabf0c2021-03-17 14:55:14 +053063 isRootDevice bool
Maninder0aabf0c2021-03-17 14:55:14 +053064 adapterMgr *adapter.Manager
65 deviceMgr *Manager
66 dbProxy *model.Proxy
67 exitChannel chan int
68 device *voltha.Device
69 requestQueue *coreutils.RequestQueue
khenaidood948f772021-08-11 17:49:24 -040070 internalTimeout time.Duration
71 rpcTimeout time.Duration
Maninder0aabf0c2021-03-17 14:55:14 +053072 startOnce sync.Once
73 stopOnce sync.Once
74 stopped bool
75 stopReconciling chan int
76 stopReconcilingMutex sync.RWMutex
77 config *config.RWCoreFlags
Mahir Gunyel03de0d32020-06-03 01:36:59 -070078
khenaidoo7585a962021-06-10 16:15:38 -040079 flowCache *flow.Cache
80 groupCache *group.Cache
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053081 portLoader *port.Loader
82 transientStateLoader *transientstate.Loader
khenaidoob9203542018-09-17 22:56:37 -040083}
84
Kent Hagerman2b216042020-04-03 18:28:56 -040085//newAgent creates a new device agent. The device will be initialized when start() is called.
khenaidood948f772021-08-11 17:49:24 -040086func newAgent(device *voltha.Device, deviceMgr *Manager, dbPath *model.Path, deviceProxy *model.Proxy, internalTimeout, rpcTimeout time.Duration) *Agent {
Kent Hagerman2a07b862020-06-19 15:23:07 -040087 deviceID := device.Id
88 if deviceID == "" {
89 deviceID = coreutils.CreateDeviceID()
Stephane Barbarie1ab43272018-12-08 21:42:13 -050090 }
Scott Baker80678602019-11-14 16:57:36 -080091
Kent Hagerman2a07b862020-06-19 15:23:07 -040092 return &Agent{
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053093 deviceID: deviceID,
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053094 isRootDevice: device.Root,
95 parentID: device.ParentId,
96 deviceType: device.Type,
khenaidood948f772021-08-11 17:49:24 -040097 adapterEndpoint: device.AdapterEndpoint,
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053098 deviceMgr: deviceMgr,
99 adapterMgr: deviceMgr.adapterMgr,
100 exitChannel: make(chan int, 1),
101 dbProxy: deviceProxy,
khenaidood948f772021-08-11 17:49:24 -0400102 internalTimeout: internalTimeout,
103 rpcTimeout: rpcTimeout,
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530104 device: proto.Clone(device).(*voltha.Device),
105 requestQueue: coreutils.NewRequestQueue(),
Maninder0aabf0c2021-03-17 14:55:14 +0530106 config: deviceMgr.config,
khenaidoo7585a962021-06-10 16:15:38 -0400107 flowCache: flow.NewCache(),
108 groupCache: group.NewCache(),
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530109 portLoader: port.NewLoader(dbPath.SubPath("ports").Proxy(deviceID)),
110 transientStateLoader: transientstate.NewLoader(dbPath.SubPath("core").Proxy("transientstate"), deviceID),
Kent Hagerman2a07b862020-06-19 15:23:07 -0400111 }
khenaidoob9203542018-09-17 22:56:37 -0400112}
113
khenaidoo442e7c72020-03-10 16:13:48 -0400114// start() saves the device to the data model and registers for callbacks on that device if deviceToCreate!=nil.
115// Otherwise, it will load the data from the dB and setup the necessary callbacks and proxies. Returns the device that
Scott Baker80678602019-11-14 16:57:36 -0800116// was started.
khenaidoo7585a962021-06-10 16:15:38 -0400117func (agent *Agent) start(ctx context.Context, deviceExist bool, deviceToCreate *voltha.Device) (*voltha.Device, error) {
khenaidoo442e7c72020-03-10 16:13:48 -0400118 needToStart := false
119 if agent.startOnce.Do(func() { needToStart = true }); !needToStart {
Kent Hagermancba2f302020-07-28 13:37:36 -0400120 return agent.getDeviceReadOnly(ctx)
khenaidoo442e7c72020-03-10 16:13:48 -0400121 }
122 var startSucceeded bool
123 defer func() {
124 if !startSucceeded {
125 if err := agent.stop(ctx); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000126 logger.Errorw(ctx, "failed-to-cleanup-after-unsuccessful-start", log.Fields{"device-id": agent.deviceID, "error": err})
khenaidoo442e7c72020-03-10 16:13:48 -0400127 }
128 }
129 }()
khenaidoo7585a962021-06-10 16:15:38 -0400130 if deviceExist {
131 device := deviceToCreate
132 if device == nil {
133 // Load from dB
134 device = &voltha.Device{}
135 have, err := agent.dbProxy.Get(ctx, agent.deviceID, device)
136 if err != nil {
137 return nil, err
138 } else if !have {
139 return nil, status.Errorf(codes.NotFound, "device-%s", agent.deviceID)
140 }
khenaidood948f772021-08-11 17:49:24 -0400141 logger.Infow(ctx, "device-loaded-from-db", log.Fields{"device-id": agent.deviceID, "adapter-endpoint": device.AdapterEndpoint, "type": device.Type})
Thomas Lee Se5a44012019-11-07 20:32:24 +0530142 }
khenaidood948f772021-08-11 17:49:24 -0400143 agent.deviceType = device.Type
144 agent.adapterEndpoint = device.AdapterEndpoint
Kent Hagerman4f355f52020-03-30 16:01:33 -0400145 agent.device = proto.Clone(device).(*voltha.Device)
khenaidoo7585a962021-06-10 16:15:38 -0400146 // load the ports from KV to cache
Kent Hagerman2a07b862020-06-19 15:23:07 -0400147 agent.portLoader.Load(ctx)
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530148 agent.transientStateLoader.Load(ctx)
khenaidoo297cd252019-02-07 22:10:23 -0500149 } else {
Scott Baker80678602019-11-14 16:57:36 -0800150 // Create a new device
Maninder9a1bc0d2020-10-26 11:34:02 +0530151 var desc string
khenaidood948f772021-08-11 17:49:24 -0400152 var err error
Maninder9a1bc0d2020-10-26 11:34:02 +0530153 prevState := common.AdminState_UNKNOWN
154 currState := common.AdminState_UNKNOWN
khenaidood948f772021-08-11 17:49:24 -0400155 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
Maninder9a1bc0d2020-10-26 11:34:02 +0530156
khenaidood948f772021-08-11 17:49:24 -0400157 defer func() { agent.logDeviceUpdate(ctx, &prevState, &currState, requestStatus, err, desc) }()
Maninder9a1bc0d2020-10-26 11:34:02 +0530158
Kent Hagermanf5a67352020-04-30 15:15:26 -0400159 // Assumption is that AdminState, FlowGroups, and Flows are uninitialized since this
160 // is a new device, so populate them here before passing the device to ldProxy.Set.
Kent Hagerman2b216042020-04-03 18:28:56 -0400161 // agent.deviceId will also have been set during newAgent().
khenaidoo7585a962021-06-10 16:15:38 -0400162 device := (proto.Clone(deviceToCreate)).(*voltha.Device)
npujar1d86a522019-11-14 17:11:16 +0530163 device.Id = agent.deviceID
Scott Baker80678602019-11-14 16:57:36 -0800164 device.AdminState = voltha.AdminState_PREPROVISIONED
Maninder9a1bc0d2020-10-26 11:34:02 +0530165 currState = device.AdminState
Scott Baker80678602019-11-14 16:57:36 -0800166 if !deviceToCreate.GetRoot() && deviceToCreate.ProxyAddress != nil {
167 // Set the default vlan ID to the one specified by the parent adapter. It can be
168 // overwritten by the child adapter during a device update request
169 device.Vlan = deviceToCreate.ProxyAddress.ChannelId
170 }
171
khenaidood948f772021-08-11 17:49:24 -0400172 // Save the device to the model
173 if err = agent.dbProxy.Set(ctx, agent.deviceID, device); err != nil {
174 err = status.Errorf(codes.Aborted, "failed-adding-device-%s: %s", agent.deviceID, err)
175 return nil, err
khenaidoo297cd252019-02-07 22:10:23 -0500176 }
Mahir Gunyelb0343bf2021-05-11 14:14:26 -0700177 _ = agent.deviceMgr.Agent.SendDeviceStateChangeEvent(ctx, device.OperStatus, device.ConnectStatus, prevState, device, time.Now().Unix())
khenaidood948f772021-08-11 17:49:24 -0400178 requestStatus.Code = common.OperationResp_OPERATION_SUCCESS
khenaidoo442e7c72020-03-10 16:13:48 -0400179 agent.device = device
khenaidoob9203542018-09-17 22:56:37 -0400180 }
khenaidoo442e7c72020-03-10 16:13:48 -0400181 startSucceeded = true
Rohan Agrawalcf12f202020-08-03 04:42:01 +0000182 log.EnrichSpan(ctx, log.Fields{"device-id": agent.deviceID})
Rohan Agrawal31f21802020-06-12 05:38:46 +0000183 logger.Debugw(ctx, "device-agent-started", log.Fields{"device-id": agent.deviceID})
khenaidoo442e7c72020-03-10 16:13:48 -0400184
Kent Hagermancba2f302020-07-28 13:37:36 -0400185 return agent.getDeviceReadOnly(ctx)
khenaidoob9203542018-09-17 22:56:37 -0400186}
187
khenaidoo4d4802d2018-10-04 21:59:49 -0400188// stop stops the device agent. Not much to do for now
Kent Hagerman2b216042020-04-03 18:28:56 -0400189func (agent *Agent) stop(ctx context.Context) error {
khenaidoo442e7c72020-03-10 16:13:48 -0400190 needToStop := false
191 if agent.stopOnce.Do(func() { needToStop = true }); !needToStop {
192 return nil
193 }
194 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
195 return err
196 }
197 defer agent.requestQueue.RequestComplete()
khenaidoo49085352020-01-13 19:15:43 -0500198
Himani Chawlab4c25912020-11-12 17:16:38 +0530199 logger.Infow(ctx, "stopping-device-agent", log.Fields{"device-id": agent.deviceID, "parent-id": agent.parentID})
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530200 // Remove the device transient loader
201 if err := agent.deleteTransientState(ctx); err != nil {
202 return err
203 }
khenaidoo0a822f92019-05-08 15:15:57 -0400204 // Remove the device from the KV store
Kent Hagermanf5a67352020-04-30 15:15:26 -0400205 if err := agent.dbProxy.Remove(ctx, agent.deviceID); err != nil {
khenaidoo442e7c72020-03-10 16:13:48 -0400206 return err
Thomas Lee Se5a44012019-11-07 20:32:24 +0530207 }
khenaidoo442e7c72020-03-10 16:13:48 -0400208
khenaidoo442e7c72020-03-10 16:13:48 -0400209 close(agent.exitChannel)
210
211 agent.stopped = true
212
Rohan Agrawal31f21802020-06-12 05:38:46 +0000213 logger.Infow(ctx, "device-agent-stopped", log.Fields{"device-id": agent.deviceID, "parent-id": agent.parentID})
khenaidoo442e7c72020-03-10 16:13:48 -0400214
215 return nil
khenaidoob9203542018-09-17 22:56:37 -0400216}
217
Scott Baker80678602019-11-14 16:57:36 -0800218// Load the most recent state from the KVStore for the device.
Kent Hagerman2b216042020-04-03 18:28:56 -0400219func (agent *Agent) reconcileWithKVStore(ctx context.Context) {
khenaidoo442e7c72020-03-10 16:13:48 -0400220 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000221 logger.Warnw(ctx, "request-aborted", log.Fields{"device-id": agent.deviceID, "error": err})
khenaidoo442e7c72020-03-10 16:13:48 -0400222 return
223 }
224 defer agent.requestQueue.RequestComplete()
Rohan Agrawal31f21802020-06-12 05:38:46 +0000225 logger.Debug(ctx, "reconciling-device-agent-devicetype")
Scott Baker80678602019-11-14 16:57:36 -0800226 // TODO: context timeout
Kent Hagerman4f355f52020-03-30 16:01:33 -0400227 device := &voltha.Device{}
Kent Hagermanf5a67352020-04-30 15:15:26 -0400228 if have, err := agent.dbProxy.Get(ctx, agent.deviceID, device); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000229 logger.Errorw(ctx, "kv-get-failed", log.Fields{"device-id": agent.deviceID, "error": err})
Thomas Lee Se5a44012019-11-07 20:32:24 +0530230 return
Kent Hagerman4f355f52020-03-30 16:01:33 -0400231 } else if !have {
232 return // not found in kv
Thomas Lee Se5a44012019-11-07 20:32:24 +0530233 }
Kent Hagerman4f355f52020-03-30 16:01:33 -0400234
khenaidood948f772021-08-11 17:49:24 -0400235 agent.deviceType = device.Type
Kent Hagerman4f355f52020-03-30 16:01:33 -0400236 agent.device = device
khenaidood948f772021-08-11 17:49:24 -0400237 agent.adapterEndpoint = device.AdapterEndpoint
Kent Hagerman2a07b862020-06-19 15:23:07 -0400238 agent.portLoader.Load(ctx)
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530239 agent.transientStateLoader.Load(ctx)
240
Rohan Agrawal31f21802020-06-12 05:38:46 +0000241 logger.Debugw(ctx, "reconciled-device-agent-devicetype", log.Fields{"device-id": agent.deviceID, "type": agent.deviceType})
Scott Baker80678602019-11-14 16:57:36 -0800242}
243
khenaidoo442e7c72020-03-10 16:13:48 -0400244// onSuccess is a common callback for scenarios where we receive a nil response following a request to an adapter
khenaidood948f772021-08-11 17:49:24 -0400245func (agent *Agent) onSuccess(ctx context.Context, prevState, currState *common.AdminState_Types, deviceUpdateLog bool) {
246 if deviceUpdateLog {
247 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_SUCCESS}
248 desc := "adapter-response"
249 agent.logDeviceUpdate(ctx, prevState, currState, requestStatus, nil, desc)
250 return
251 }
252 logger.Debugw(ctx, "successful-operation", log.Fields{"device-id": agent.deviceID, "rpc": coreutils.GetRPCMetadataFromContext(ctx)})
khenaidoo442e7c72020-03-10 16:13:48 -0400253}
254
255// onFailure is a common callback for scenarios where we receive an error response following a request to an adapter
256// and the only action required is to publish the failed result on kafka
khenaidood948f772021-08-11 17:49:24 -0400257func (agent *Agent) onFailure(ctx context.Context, err error, prevState, currState *common.AdminState_Types, deviceUpdateLog bool) {
258 // Send an event on kafka
259 rpce := agent.deviceMgr.NewRPCEvent(ctx, agent.deviceID, err.Error(), nil)
260 go agent.deviceMgr.SendRPCEvent(ctx, "RPC_ERROR_RAISE_EVENT", rpce,
261 voltha.EventCategory_COMMUNICATION, nil, time.Now().Unix())
khenaidoo442e7c72020-03-10 16:13:48 -0400262
khenaidood948f772021-08-11 17:49:24 -0400263 // Log the device update event
264 if deviceUpdateLog {
265 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
266 desc := "adapter-response"
267 agent.logDeviceUpdate(ctx, prevState, currState, requestStatus, err, desc)
268 return
khenaidoo442e7c72020-03-10 16:13:48 -0400269 }
khenaidood948f772021-08-11 17:49:24 -0400270 logger.Errorw(ctx, "failed-operation", log.Fields{"error": err, "device-id": agent.deviceID, "rpc": coreutils.GetRPCMetadataFromContext(ctx)})
khenaidoo442e7c72020-03-10 16:13:48 -0400271}
272
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530273// onDeleteSuccess is a common callback for scenarios where we receive a nil response following a delete request
274// to an adapter.
khenaidood948f772021-08-11 17:49:24 -0400275func (agent *Agent) onDeleteSuccess(ctx context.Context, prevState, currState *common.AdminState_Types) {
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530276 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
khenaidood948f772021-08-11 17:49:24 -0400277 logger.Errorw(ctx, "delete-device-failure", log.Fields{"device-id": agent.deviceID, "error": err})
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530278 }
279 previousDeviceTransientState := agent.getTransientState()
280 newDevice := agent.cloneDeviceWithoutLock()
281 if err := agent.updateDeviceWithTransientStateAndReleaseLock(ctx, newDevice,
khenaidood948f772021-08-11 17:49:24 -0400282 core.DeviceTransientState_DELETING_POST_ADAPTER_RESPONSE, previousDeviceTransientState); err != nil {
283 logger.Errorw(ctx, "delete-device-failure", log.Fields{"device-id": agent.deviceID, "error": err})
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530284 }
khenaidood948f772021-08-11 17:49:24 -0400285 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_SUCCESS}
286 desc := "adapter-response"
287 agent.logDeviceUpdate(ctx, prevState, currState, requestStatus, nil, desc)
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530288}
289
290// onDeleteFailure is a common callback for scenarios where we receive an error response following a delete request
291// to an adapter and the only action required is to return the error response.
khenaidood948f772021-08-11 17:49:24 -0400292func (agent *Agent) onDeleteFailure(ctx context.Context, err error, prevState, currState *common.AdminState_Types) {
293 logger.Errorw(ctx, "rpc-failed", log.Fields{"rpc": coreutils.GetRPCMetadataFromContext(ctx), "device-id": agent.deviceID, "error": err})
294
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530295 //Only updating of transient state is required, no transition.
khenaidood948f772021-08-11 17:49:24 -0400296 if er := agent.updateTransientState(ctx, core.DeviceTransientState_DELETE_FAILED); er != nil {
297 logger.Errorw(ctx, "failed-to-update-transient-state-as-delete-failed", log.Fields{"device-id": agent.deviceID, "error": er})
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530298 }
khenaidood948f772021-08-11 17:49:24 -0400299 rpce := agent.deviceMgr.NewRPCEvent(ctx, agent.deviceID, err.Error(), nil)
300 go agent.deviceMgr.SendRPCEvent(ctx, "RPC_ERROR_RAISE_EVENT", rpce,
301 voltha.EventCategory_COMMUNICATION, nil, time.Now().Unix())
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530302
khenaidood948f772021-08-11 17:49:24 -0400303 // Log the device update event
304 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
305 desc := "adapter-response"
306 agent.logDeviceUpdate(ctx, prevState, currState, requestStatus, err, desc)
Maninder9a1bc0d2020-10-26 11:34:02 +0530307}
308
Kent Hagermancba2f302020-07-28 13:37:36 -0400309// getDeviceReadOnly returns a device which MUST NOT be modified, but is safe to keep forever.
310func (agent *Agent) getDeviceReadOnly(ctx context.Context) (*voltha.Device, error) {
khenaidoo442e7c72020-03-10 16:13:48 -0400311 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
312 return nil, err
313 }
314 defer agent.requestQueue.RequestComplete()
Kent Hagermancba2f302020-07-28 13:37:36 -0400315 return agent.device, nil
khenaidoo92e62c52018-10-03 14:02:54 -0400316}
317
Kent Hagermancba2f302020-07-28 13:37:36 -0400318// getDeviceReadOnlyWithoutLock returns a device which MUST NOT be modified, but is safe to keep forever. This is very efficient.
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400319// The device lock MUST be held by the caller.
Kent Hagermancba2f302020-07-28 13:37:36 -0400320func (agent *Agent) getDeviceReadOnlyWithoutLock() *voltha.Device {
khenaidoo0db4c812020-05-27 15:27:30 -0400321 return agent.device
khenaidoo92e62c52018-10-03 14:02:54 -0400322}
323
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400324// cloneDeviceWithoutLock returns a copy of the device which is safe to modify.
325// The device lock MUST be held by the caller.
326func (agent *Agent) cloneDeviceWithoutLock() *voltha.Device {
327 return proto.Clone(agent.device).(*voltha.Device)
328}
329
khenaidood948f772021-08-11 17:49:24 -0400330func (agent *Agent) updateDeviceTypeAndEndpoint(ctx context.Context) error {
331 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
332 return err
333 }
334 changed := false
335 cloned := agent.cloneDeviceWithoutLock()
336 if cloned.Type == "" {
337 adapterType, err := agent.adapterMgr.GetAdapterType(cloned.Type)
338 if err != nil {
339 agent.requestQueue.RequestComplete()
340 return err
341 }
342 cloned.Type = adapterType
343 changed = true
344 }
345
346 if cloned.AdapterEndpoint == "" {
347 var err error
348 if cloned.AdapterEndpoint, err = agent.adapterMgr.GetAdapterEndpoint(ctx, cloned.Id, cloned.Type); err != nil {
349 agent.requestQueue.RequestComplete()
350 return err
351 }
352 agent.adapterEndpoint = cloned.AdapterEndpoint
353 changed = true
354 }
355
356 if changed {
357 return agent.updateDeviceAndReleaseLock(ctx, cloned)
358 }
359 agent.requestQueue.RequestComplete()
360 return nil
361}
362
khenaidoo3ab34882019-05-02 21:33:30 -0400363// enableDevice activates a preprovisioned or a disable device
Kent Hagerman2b216042020-04-03 18:28:56 -0400364func (agent *Agent) enableDevice(ctx context.Context) error {
Maninder9a1bc0d2020-10-26 11:34:02 +0530365 //To preserve and use oldDevice state as prev state in new device
khenaidood948f772021-08-11 17:49:24 -0400366 var err error
Maninder9a1bc0d2020-10-26 11:34:02 +0530367 var desc string
khenaidood948f772021-08-11 17:49:24 -0400368 var prevAdminState, currAdminState common.AdminState_Types
369 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
Maninder9a1bc0d2020-10-26 11:34:02 +0530370
khenaidood948f772021-08-11 17:49:24 -0400371 defer func() { agent.logDeviceUpdate(ctx, &prevAdminState, &currAdminState, requestStatus, err, desc) }()
Maninder9a1bc0d2020-10-26 11:34:02 +0530372
khenaidood948f772021-08-11 17:49:24 -0400373 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
khenaidoo442e7c72020-03-10 16:13:48 -0400374 return err
375 }
Himani Chawlab4c25912020-11-12 17:16:38 +0530376 logger.Debugw(ctx, "enable-device", log.Fields{"device-id": agent.deviceID})
khenaidoo21d51152019-02-01 13:48:37 -0500377
Kent Hagermancba2f302020-07-28 13:37:36 -0400378 oldDevice := agent.getDeviceReadOnlyWithoutLock()
khenaidood948f772021-08-11 17:49:24 -0400379 prevAdminState = oldDevice.AdminState
Maninder9a1bc0d2020-10-26 11:34:02 +0530380
Maninder2195ccc2021-06-23 20:23:01 +0530381 if !agent.proceedWithRequest(oldDevice) {
382 agent.requestQueue.RequestComplete()
khenaidood948f772021-08-11 17:49:24 -0400383 err = status.Errorf(codes.FailedPrecondition, "cannot complete operation as device deletion is in progress or reconciling is in progress/failed: %s", agent.deviceID)
384 return err
Maninder2195ccc2021-06-23 20:23:01 +0530385 }
Mahir Gunyel92dd1212021-10-22 11:42:56 -0700386 //vol-4275 TST meeting 08/04/2021: Let EnableDevice to be called again if device is in FAILED operational state,
387 //even the admin state is ENABLED.
388 if oldDevice.AdminState == voltha.AdminState_ENABLED && oldDevice.OperStatus != voltha.OperStatus_FAILED {
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400389 logger.Warnw(ctx, "device-already-enabled", log.Fields{"device-id": agent.deviceID})
390 agent.requestQueue.RequestComplete()
khenaidood948f772021-08-11 17:49:24 -0400391 err = status.Errorf(codes.FailedPrecondition, fmt.Sprintf("cannot-enable-an-already-enabled-device: %s", oldDevice.Id))
Matteo Scandolod525ae32020-04-02 17:27:29 -0700392 return err
npujar1d86a522019-11-14 17:11:16 +0530393 }
394
khenaidood948f772021-08-11 17:49:24 -0400395 // Verify whether there is a device type that supports this device type
396 _, err = agent.adapterMgr.GetAdapterType(oldDevice.Type)
397 if err != nil {
398 agent.requestQueue.RequestComplete()
399 return err
400 }
401
402 // Update device adapter endpoint if not set. This is set once by the Core and use as is by the adapters. E.g if this is a
403 // child device then the parent adapter will use this device's adapter endpoint (set here) to communicate with it.
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400404 newDevice := agent.cloneDeviceWithoutLock()
khenaidood948f772021-08-11 17:49:24 -0400405 if newDevice.AdapterEndpoint == "" {
406 if newDevice.AdapterEndpoint, err = agent.adapterMgr.GetAdapterEndpoint(ctx, newDevice.Id, newDevice.Type); err != nil {
407 agent.requestQueue.RequestComplete()
408 return err
409 }
410 agent.adapterEndpoint = newDevice.AdapterEndpoint
411 }
npujar1d86a522019-11-14 17:11:16 +0530412
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400413 // Update the Admin State and set the operational state to activating before sending the request to the Adapters
414 newDevice.AdminState = voltha.AdminState_ENABLED
415 newDevice.OperStatus = voltha.OperStatus_ACTIVATING
Maninder9a1bc0d2020-10-26 11:34:02 +0530416
khenaidoo442e7c72020-03-10 16:13:48 -0400417 // Adopt the device if it was in pre-provision state. In all other cases, try to re-enable it.
khenaidood948f772021-08-11 17:49:24 -0400418 client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
khenaidoo442e7c72020-03-10 16:13:48 -0400419 if err != nil {
khenaidood948f772021-08-11 17:49:24 -0400420 logger.Errorw(ctx, "grpc-client-nil",
421 log.Fields{
422 "error": err,
423 "device-id": agent.deviceID,
424 "device-type": agent.deviceType,
425 "adapter-endpoint": newDevice.AdapterEndpoint,
426 })
427 agent.requestQueue.RequestComplete()
khenaidoo442e7c72020-03-10 16:13:48 -0400428 return err
429 }
khenaidood948f772021-08-11 17:49:24 -0400430 subCtx, cancel := context.WithTimeout(coreutils.WithAllMetadataFromContext(ctx), agent.rpcTimeout)
431 requestStatus.Code = common.OperationResp_OPERATION_IN_PROGRESS
432 go func() {
433 defer cancel()
434 var err error
435 if oldDevice.AdminState == voltha.AdminState_PREPROVISIONED {
436 _, err = client.AdoptDevice(subCtx, newDevice)
437 } else {
438 _, err = client.ReEnableDevice(subCtx, newDevice)
439 }
440 if err == nil {
441 agent.onSuccess(subCtx, nil, nil, true)
442 } else {
443 agent.onFailure(subCtx, err, nil, nil, true)
Himani Chawlab4c25912020-11-12 17:16:38 +0530444 }
445 }()
khenaidood948f772021-08-11 17:49:24 -0400446
447 // Update device
448 if err = agent.updateDeviceAndReleaseLock(ctx, newDevice); err != nil {
449 return err
khenaidoo2c6a0992019-04-29 13:46:56 -0400450 }
khenaidood948f772021-08-11 17:49:24 -0400451 currAdminState = newDevice.AdminState
452 return nil
khenaidoo2c6a0992019-04-29 13:46:56 -0400453}
454
A R Karthick5c28f552019-12-11 22:47:44 -0800455//addFlowsAndGroups adds the "newFlows" and "newGroups" from the existing flows/groups and sends the update to the
456//adapters
khenaidoo9beaaf12021-10-19 17:32:01 -0400457func (agent *Agent) addFlowsAndGroups(ctx context.Context, newFlows []*ofp.OfpFlowStats, newGroups []*ofp.OfpGroupEntry, flowMetadata *ofp.FlowMetadata) error {
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700458 var flwResponse, grpResponse coreutils.Response
459 var err error
460 //if new flow list is empty then the called function returns quickly
461 if flwResponse, err = agent.addFlowsToAdapter(ctx, newFlows, flowMetadata); err != nil {
A R Karthick5c28f552019-12-11 22:47:44 -0800462 return err
463 }
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700464 //if new group list is empty then the called function returns quickly
465 if grpResponse, err = agent.addGroupsToAdapter(ctx, newGroups, flowMetadata); err != nil {
466 return err
467 }
khenaidood948f772021-08-11 17:49:24 -0400468 if errs := coreutils.WaitForNilOrErrorResponses(agent.rpcTimeout, flwResponse, grpResponse); errs != nil {
469 logger.Warnw(ctx, "adapter-response", log.Fields{"device-id": agent.deviceID, "result": errs})
khenaidoo442e7c72020-03-10 16:13:48 -0400470 return status.Errorf(codes.Aborted, "flow-failure-device-%s", agent.deviceID)
khenaidoo0458db62019-06-20 08:50:36 -0400471 }
khenaidoo0458db62019-06-20 08:50:36 -0400472 return nil
473}
474
A R Karthick5c28f552019-12-11 22:47:44 -0800475//deleteFlowsAndGroups removes the "flowsToDel" and "groupsToDel" from the existing flows/groups and sends the update to the
476//adapters
khenaidoo9beaaf12021-10-19 17:32:01 -0400477func (agent *Agent) deleteFlowsAndGroups(ctx context.Context, flowsToDel []*ofp.OfpFlowStats, groupsToDel []*ofp.OfpGroupEntry, flowMetadata *ofp.FlowMetadata) error {
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700478 var flwResponse, grpResponse coreutils.Response
479 var err error
480 if flwResponse, err = agent.deleteFlowsFromAdapter(ctx, flowsToDel, flowMetadata); err != nil {
A R Karthick5c28f552019-12-11 22:47:44 -0800481 return err
482 }
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700483 if grpResponse, err = agent.deleteGroupsFromAdapter(ctx, groupsToDel, flowMetadata); err != nil {
484 return err
485 }
486
khenaidood948f772021-08-11 17:49:24 -0400487 if res := coreutils.WaitForNilOrErrorResponses(agent.rpcTimeout, flwResponse, grpResponse); res != nil {
khenaidoo0458db62019-06-20 08:50:36 -0400488 return status.Errorf(codes.Aborted, "errors-%s", res)
489 }
490 return nil
khenaidoo0458db62019-06-20 08:50:36 -0400491}
492
A R Karthick5c28f552019-12-11 22:47:44 -0800493//updateFlowsAndGroups replaces the existing flows and groups with "updatedFlows" and "updatedGroups" respectively. It
494//also sends the updates to the adapters
khenaidoo9beaaf12021-10-19 17:32:01 -0400495func (agent *Agent) updateFlowsAndGroups(ctx context.Context, updatedFlows []*ofp.OfpFlowStats, updatedGroups []*ofp.OfpGroupEntry, flowMetadata *ofp.FlowMetadata) error {
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700496 var flwResponse, grpResponse coreutils.Response
497 var err error
498 if flwResponse, err = agent.updateFlowsToAdapter(ctx, updatedFlows, flowMetadata); err != nil {
A R Karthick5c28f552019-12-11 22:47:44 -0800499 return err
500 }
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700501 if grpResponse, err = agent.updateGroupsToAdapter(ctx, updatedGroups, flowMetadata); err != nil {
502 return err
503 }
504
khenaidood948f772021-08-11 17:49:24 -0400505 if res := coreutils.WaitForNilOrErrorResponses(agent.rpcTimeout, flwResponse, grpResponse); res != nil {
khenaidoo0458db62019-06-20 08:50:36 -0400506 return status.Errorf(codes.Aborted, "errors-%s", res)
507 }
508 return nil
khenaidoo19d7b632018-10-30 10:49:50 -0400509}
510
khenaidoo4d4802d2018-10-04 21:59:49 -0400511//disableDevice disable a device
Kent Hagerman2b216042020-04-03 18:28:56 -0400512func (agent *Agent) disableDevice(ctx context.Context) error {
khenaidood948f772021-08-11 17:49:24 -0400513 var err error
Maninder9a1bc0d2020-10-26 11:34:02 +0530514 var desc string
khenaidood948f772021-08-11 17:49:24 -0400515 var prevAdminState, currAdminState common.AdminState_Types
516 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
517 defer func() { agent.logDeviceUpdate(ctx, &prevAdminState, &currAdminState, requestStatus, err, desc) }()
Maninder9a1bc0d2020-10-26 11:34:02 +0530518
khenaidood948f772021-08-11 17:49:24 -0400519 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
khenaidoo442e7c72020-03-10 16:13:48 -0400520 return err
521 }
Himani Chawlab4c25912020-11-12 17:16:38 +0530522 logger.Debugw(ctx, "disable-device", log.Fields{"device-id": agent.deviceID})
khenaidoo6e55d9e2019-12-12 18:26:26 -0500523
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400524 cloned := agent.cloneDeviceWithoutLock()
khenaidood948f772021-08-11 17:49:24 -0400525 prevAdminState = agent.device.AdminState
khenaidoo6e55d9e2019-12-12 18:26:26 -0500526
Maninder2195ccc2021-06-23 20:23:01 +0530527 if !agent.proceedWithRequest(cloned) {
khenaidood948f772021-08-11 17:49:24 -0400528 err = status.Errorf(codes.FailedPrecondition, "cannot complete operation as device deletion is in progress or reconciling is in progress/failed: %s", agent.deviceID)
Maninder2195ccc2021-06-23 20:23:01 +0530529 agent.requestQueue.RequestComplete()
khenaidood948f772021-08-11 17:49:24 -0400530 return err
Maninder2195ccc2021-06-23 20:23:01 +0530531 }
532
khenaidoo6e55d9e2019-12-12 18:26:26 -0500533 if cloned.AdminState == voltha.AdminState_DISABLED {
Maninder9a1bc0d2020-10-26 11:34:02 +0530534 desc = "device-already-disabled"
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400535 agent.requestQueue.RequestComplete()
npujar1d86a522019-11-14 17:11:16 +0530536 return nil
537 }
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530538 if cloned.AdminState == voltha.AdminState_PREPROVISIONED {
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400539 agent.requestQueue.RequestComplete()
khenaidood948f772021-08-11 17:49:24 -0400540 err = status.Errorf(codes.FailedPrecondition, "deviceId:%s, invalid-admin-state:%s", agent.deviceID, cloned.AdminState)
541 return err
npujar1d86a522019-11-14 17:11:16 +0530542 }
Maninder0aabf0c2021-03-17 14:55:14 +0530543
npujar1d86a522019-11-14 17:11:16 +0530544 // Update the Admin State and operational state before sending the request out
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400545 cloned.AdminState = voltha.AdminState_DISABLED
546 cloned.OperStatus = voltha.OperStatus_UNKNOWN
Maninder9a1bc0d2020-10-26 11:34:02 +0530547
khenaidood948f772021-08-11 17:49:24 -0400548 client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
khenaidoo442e7c72020-03-10 16:13:48 -0400549 if err != nil {
khenaidood948f772021-08-11 17:49:24 -0400550 logger.Errorw(ctx, "grpc-client-nil",
551 log.Fields{
552 "error": err,
553 "device-id": agent.deviceID,
554 "device-type": agent.deviceType,
555 "adapter-endpoint": cloned.AdapterEndpoint,
556 })
557 agent.requestQueue.RequestComplete()
npujar1d86a522019-11-14 17:11:16 +0530558 return err
khenaidoo0a822f92019-05-08 15:15:57 -0400559 }
khenaidood948f772021-08-11 17:49:24 -0400560 subCtx, cancel := context.WithTimeout(coreutils.WithAllMetadataFromContext(ctx), agent.rpcTimeout)
561 requestStatus.Code = common.OperationResp_OPERATION_IN_PROGRESS
562 go func() {
563 defer cancel()
564 _, err := client.DisableDevice(subCtx, cloned)
565 if err == nil {
566 agent.onSuccess(subCtx, nil, nil, true)
567 } else {
568 agent.onFailure(subCtx, err, nil, nil, true)
569 }
570 }()
Maninder9a1bc0d2020-10-26 11:34:02 +0530571
khenaidood948f772021-08-11 17:49:24 -0400572 // Update device
573 if err = agent.updateDeviceAndReleaseLock(ctx, cloned); err != nil {
574 return err
575 }
576 currAdminState = cloned.AdminState
khenaidoo0a822f92019-05-08 15:15:57 -0400577
khenaidoo92e62c52018-10-03 14:02:54 -0400578 return nil
579}
580
Kent Hagerman2b216042020-04-03 18:28:56 -0400581func (agent *Agent) rebootDevice(ctx context.Context) error {
Maninder9a1bc0d2020-10-26 11:34:02 +0530582 var desc string
khenaidood948f772021-08-11 17:49:24 -0400583 var err error
584 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
585 defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
Maninder9a1bc0d2020-10-26 11:34:02 +0530586
khenaidood948f772021-08-11 17:49:24 -0400587 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
Maninder9a1bc0d2020-10-26 11:34:02 +0530588 desc = err.Error()
npujar1d86a522019-11-14 17:11:16 +0530589 return err
khenaidoo4d4802d2018-10-04 21:59:49 -0400590 }
khenaidoo442e7c72020-03-10 16:13:48 -0400591 defer agent.requestQueue.RequestComplete()
Himani Chawlab4c25912020-11-12 17:16:38 +0530592 logger.Debugw(ctx, "reboot-device", log.Fields{"device-id": agent.deviceID})
khenaidoo442e7c72020-03-10 16:13:48 -0400593
Kent Hagermancba2f302020-07-28 13:37:36 -0400594 device := agent.getDeviceReadOnlyWithoutLock()
Maninder2195ccc2021-06-23 20:23:01 +0530595
596 if !agent.proceedWithRequest(device) {
khenaidood948f772021-08-11 17:49:24 -0400597 err = status.Errorf(codes.FailedPrecondition, "cannot complete operation as device deletion is in progress or reconciling is in progress/failed:%s", agent.deviceID)
khenaidoo442e7c72020-03-10 16:13:48 -0400598 return err
599 }
Maninder9a1bc0d2020-10-26 11:34:02 +0530600
khenaidood948f772021-08-11 17:49:24 -0400601 client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
602 if err != nil {
603 logger.Errorw(ctx, "grpc-client-nil",
604 log.Fields{
605 "error": err,
606 "device-id": agent.deviceID,
607 "device-type": agent.deviceType,
608 "adapter-endpoint": device.AdapterEndpoint,
609 })
610 return err
611 }
612 subCtx, cancel := context.WithTimeout(coreutils.WithAllMetadataFromContext(ctx), agent.rpcTimeout)
613 requestStatus.Code = common.OperationResp_OPERATION_IN_PROGRESS
614 go func() {
615 defer cancel()
616 _, err := client.RebootDevice(subCtx, device)
617 if err == nil {
618 agent.onSuccess(subCtx, nil, nil, true)
619 } else {
620 agent.onFailure(subCtx, err, nil, nil, true)
621 }
622 }()
khenaidoo4d4802d2018-10-04 21:59:49 -0400623 return nil
624}
625
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530626func (agent *Agent) deleteDeviceForce(ctx context.Context) error {
Himani Chawlab4c25912020-11-12 17:16:38 +0530627 logger.Debugw(ctx, "delete-device-force", log.Fields{"device-id": agent.deviceID})
Maninder9a1bc0d2020-10-26 11:34:02 +0530628
629 var desc string
khenaidood948f772021-08-11 17:49:24 -0400630 var err error
631 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
632 defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
Maninder9a1bc0d2020-10-26 11:34:02 +0530633
khenaidood948f772021-08-11 17:49:24 -0400634 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530635 return err
636 }
637 // Get the device Transient state, return err if it is DELETING
638 previousDeviceTransientState := agent.getTransientState()
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530639 device := agent.cloneDeviceWithoutLock()
khenaidood948f772021-08-11 17:49:24 -0400640 if !agent.isForceDeletingAllowed(previousDeviceTransientState, device) {
641 agent.requestQueue.RequestComplete()
642 err = status.Error(codes.FailedPrecondition, fmt.Sprintf("deviceId:%s, force deletion is in progress", agent.deviceID))
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530643 return err
644 }
Himani Chawlab4c25912020-11-12 17:16:38 +0530645
khenaidood948f772021-08-11 17:49:24 -0400646 previousAdminState := device.AdminState
647 if previousAdminState != common.AdminState_PREPROVISIONED {
khenaidoo9beaaf12021-10-19 17:32:01 -0400648 var client adapter_service.AdapterServiceClient
khenaidood948f772021-08-11 17:49:24 -0400649 client, err = agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530650 if err != nil {
khenaidood948f772021-08-11 17:49:24 -0400651 logger.Errorw(ctx, "grpc-client-nil",
652 log.Fields{
653 "error": err,
654 "device-id": agent.deviceID,
655 "device-type": agent.deviceType,
656 "adapter-endpoint": device.AdapterEndpoint,
657 })
658 agent.requestQueue.RequestComplete()
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530659 return err
660 }
khenaidood948f772021-08-11 17:49:24 -0400661 subCtx, cancel := context.WithTimeout(coreutils.WithAllMetadataFromContext(ctx), agent.rpcTimeout)
662 requestStatus.Code = common.OperationResp_OPERATION_IN_PROGRESS
663 go func() {
664 defer cancel()
665 _, err := client.DeleteDevice(subCtx, device)
666 if err == nil {
667 agent.onSuccess(subCtx, nil, nil, true)
668 } else {
669 agent.onFailure(subCtx, err, nil, nil, true)
670 }
671 }()
672 }
673
674 // Update device
675 if err = agent.updateDeviceWithTransientStateAndReleaseLock(ctx, device,
676 core.DeviceTransientState_FORCE_DELETING, previousDeviceTransientState); err != nil {
677 return err
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530678 }
679 return nil
680}
681
Kent Hagerman2b216042020-04-03 18:28:56 -0400682func (agent *Agent) deleteDevice(ctx context.Context) error {
Himani Chawlab4c25912020-11-12 17:16:38 +0530683 logger.Debugw(ctx, "delete-device", log.Fields{"device-id": agent.deviceID})
Maninder9a1bc0d2020-10-26 11:34:02 +0530684
685 var desc string
khenaidood948f772021-08-11 17:49:24 -0400686 var err error
687 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
688 defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
Maninder9a1bc0d2020-10-26 11:34:02 +0530689
khenaidood948f772021-08-11 17:49:24 -0400690 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
Maninder9a1bc0d2020-10-26 11:34:02 +0530691 desc = err.Error()
khenaidoo442e7c72020-03-10 16:13:48 -0400692 return err
693 }
Maninder0aabf0c2021-03-17 14:55:14 +0530694
Maninder2195ccc2021-06-23 20:23:01 +0530695 device := agent.cloneDeviceWithoutLock()
696
697 if !agent.proceedWithRequest(device) {
Maninder0aabf0c2021-03-17 14:55:14 +0530698 agent.requestQueue.RequestComplete()
khenaidood948f772021-08-11 17:49:24 -0400699 err = status.Errorf(codes.FailedPrecondition, "cannot complete operation as device deletion is in progress or reconciling is in progress/failed: %s", agent.deviceID)
700 return err
Maninder0aabf0c2021-03-17 14:55:14 +0530701 }
702
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530703 // Get the device Transient state, return err if it is DELETING
704 previousDeviceTransientState := agent.getTransientState()
khenaidoo6e55d9e2019-12-12 18:26:26 -0500705
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530706 previousAdminState := device.AdminState
707 // Change the device transient state to DELETING_FROM_ADAPTER state till the device is removed from adapters.
khenaidood948f772021-08-11 17:49:24 -0400708 currentDeviceTransientState := core.DeviceTransientState_DELETING_FROM_ADAPTER
khenaidoo442e7c72020-03-10 16:13:48 -0400709
khenaidood948f772021-08-11 17:49:24 -0400710 if previousAdminState == common.AdminState_PREPROVISIONED {
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530711 // Change the state to DELETING POST ADAPTER RESPONSE directly as adapters have no info of the device.
khenaidood948f772021-08-11 17:49:24 -0400712 currentDeviceTransientState = core.DeviceTransientState_DELETING_POST_ADAPTER_RESPONSE
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530713 }
khenaidood948f772021-08-11 17:49:24 -0400714 // If the device was in pre-prov state (only parent device are in that state) then do not send the request to the
715 // adapter
716 if previousAdminState != common.AdminState_PREPROVISIONED {
khenaidoo9beaaf12021-10-19 17:32:01 -0400717 var client adapter_service.AdapterServiceClient
khenaidood948f772021-08-11 17:49:24 -0400718 client, err = agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
719 if err != nil {
720 logger.Errorw(ctx, "grpc-client-nil",
721 log.Fields{
722 "error": err,
723 "device-id": agent.deviceID,
724 "device-type": agent.deviceType,
725 "adapter-endpoint": device.AdapterEndpoint,
726 })
727 agent.requestQueue.RequestComplete()
728 return err
729 }
730 subCtx, cancel := context.WithTimeout(coreutils.WithAllMetadataFromContext(ctx), agent.rpcTimeout)
731 requestStatus.Code = common.OperationResp_OPERATION_IN_PROGRESS
732 go func() {
733 defer cancel()
734 _, err := client.DeleteDevice(subCtx, device)
735 if err == nil {
736 agent.onDeleteSuccess(subCtx, nil, nil)
737 } else {
738 agent.onDeleteFailure(subCtx, err, nil, nil)
739 }
740 }()
741 }
742
743 // Update device and release lock
744 if err = agent.updateDeviceWithTransientStateAndReleaseLock(ctx, device,
Himani Chawlab4c25912020-11-12 17:16:38 +0530745 currentDeviceTransientState, previousDeviceTransientState); err != nil {
Maninder9a1bc0d2020-10-26 11:34:02 +0530746 desc = err.Error()
npujar1d86a522019-11-14 17:11:16 +0530747 return err
748 }
Himani Chawlab4c25912020-11-12 17:16:38 +0530749
khenaidoo4d4802d2018-10-04 21:59:49 -0400750 return nil
751}
752
Kent Hagerman2b216042020-04-03 18:28:56 -0400753func (agent *Agent) setParentID(ctx context.Context, device *voltha.Device, parentID string) error {
khenaidoo442e7c72020-03-10 16:13:48 -0400754 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
755 return err
756 }
Himani Chawlab4c25912020-11-12 17:16:38 +0530757 logger.Debugw(ctx, "set-parent-id", log.Fields{"device-id": device.Id, "parent-id": parentID})
khenaidoo6e55d9e2019-12-12 18:26:26 -0500758
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400759 cloned := agent.cloneDeviceWithoutLock()
npujar1d86a522019-11-14 17:11:16 +0530760 cloned.ParentId = parentID
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400761 return agent.updateDeviceAndReleaseLock(ctx, cloned)
khenaidooad06fd72019-10-28 12:26:05 -0400762}
763
khenaidoo442e7c72020-03-10 16:13:48 -0400764// getSwitchCapability retrieves the switch capability of a parent device
khenaidoo9beaaf12021-10-19 17:32:01 -0400765func (agent *Agent) getSwitchCapability(ctx context.Context) (*ca.SwitchCapability, error) {
Himani Chawlab4c25912020-11-12 17:16:38 +0530766 logger.Debugw(ctx, "get-switch-capability", log.Fields{"device-id": agent.deviceID})
khenaidoo442e7c72020-03-10 16:13:48 -0400767
Kent Hagermancba2f302020-07-28 13:37:36 -0400768 device, err := agent.getDeviceReadOnly(ctx)
khenaidoo442e7c72020-03-10 16:13:48 -0400769 if err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400770 return nil, err
khenaidoob9203542018-09-17 22:56:37 -0400771 }
khenaidood948f772021-08-11 17:49:24 -0400772
773 // Get the gRPC client
774 client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
khenaidoo442e7c72020-03-10 16:13:48 -0400775 if err != nil {
776 return nil, err
777 }
778
khenaidood948f772021-08-11 17:49:24 -0400779 return client.GetOfpDeviceInfo(ctx, device)
khenaidoob9203542018-09-17 22:56:37 -0400780}
781
khenaidood948f772021-08-11 17:49:24 -0400782func (agent *Agent) onPacketFailure(ctx context.Context, err error, packet *ofp.OfpPacketOut) {
783 logger.Errorw(ctx, "packet-out-error", log.Fields{
khenaidoo442e7c72020-03-10 16:13:48 -0400784 "device-id": agent.deviceID,
khenaidood948f772021-08-11 17:49:24 -0400785 "error": err.Error(),
786 "packet": hex.EncodeToString(packet.Data),
khenaidoo442e7c72020-03-10 16:13:48 -0400787 })
khenaidood948f772021-08-11 17:49:24 -0400788 rpce := agent.deviceMgr.NewRPCEvent(ctx, agent.deviceID, err.Error(), nil)
789 go agent.deviceMgr.SendRPCEvent(ctx, "RPC_ERROR_RAISE_EVENT", rpce,
790 voltha.EventCategory_COMMUNICATION, nil, time.Now().Unix())
khenaidoo442e7c72020-03-10 16:13:48 -0400791}
792
Kent Hagerman2b216042020-04-03 18:28:56 -0400793func (agent *Agent) packetOut(ctx context.Context, outPort uint32, packet *ofp.OfpPacketOut) error {
Scott Baker80678602019-11-14 16:57:36 -0800794 if agent.deviceType == "" {
npujar467fe752020-01-16 20:17:45 +0530795 agent.reconcileWithKVStore(ctx)
Scott Baker80678602019-11-14 16:57:36 -0800796 }
khenaidoofdbad6e2018-11-06 22:26:38 -0500797 // Send packet to adapter
khenaidood948f772021-08-11 17:49:24 -0400798 client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
khenaidoo442e7c72020-03-10 16:13:48 -0400799 if err != nil {
khenaidood948f772021-08-11 17:49:24 -0400800 logger.Errorw(ctx, "grpc-client-nil",
801 log.Fields{
802 "error": err,
803 "device-id": agent.deviceID,
804 "device-type": agent.deviceType,
805 })
806 return err
khenaidoofdbad6e2018-11-06 22:26:38 -0500807 }
khenaidood948f772021-08-11 17:49:24 -0400808 subCtx, cancel := context.WithTimeout(coreutils.WithAllMetadataFromContext(ctx), agent.rpcTimeout)
809 go func() {
810 defer cancel()
khenaidoo9beaaf12021-10-19 17:32:01 -0400811 _, err := client.SendPacketOut(subCtx, &ca.PacketOut{
khenaidood948f772021-08-11 17:49:24 -0400812 DeviceId: agent.deviceID,
813 EgressPortNo: outPort,
814 Packet: packet,
815 })
816 if err == nil {
817 agent.onSuccess(subCtx, nil, nil, false)
818 } else {
819 agent.onPacketFailure(subCtx, err, packet)
820 }
821 }()
khenaidoofdbad6e2018-11-06 22:26:38 -0500822 return nil
823}
824
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400825func (agent *Agent) updateDeviceUsingAdapterData(ctx context.Context, device *voltha.Device) error {
khenaidood948f772021-08-11 17:49:24 -0400826 var err error
827 var desc string
828 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
829 defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
830
831 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400832 return err
833 }
Himani Chawlab4c25912020-11-12 17:16:38 +0530834 logger.Debugw(ctx, "update-device-using-adapter-data", log.Fields{"device-id": device.Id})
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400835
836 cloned := agent.cloneDeviceWithoutLock()
Mahir Gunyel8e2707d2019-07-25 00:36:21 -0700837 cloned.Root = device.Root
838 cloned.Vendor = device.Vendor
839 cloned.Model = device.Model
840 cloned.SerialNumber = device.SerialNumber
841 cloned.MacAddress = device.MacAddress
842 cloned.Vlan = device.Vlan
843 cloned.Reason = device.Reason
Andrea Campanella025667e2021-01-14 11:50:07 +0100844 cloned.ImageDownloads = device.ImageDownloads
khenaidood948f772021-08-11 17:49:24 -0400845 cloned.OperStatus = device.OperStatus
846 cloned.ConnectStatus = device.ConnectStatus
847 if err = agent.updateDeviceAndReleaseLock(ctx, cloned); err == nil {
848 requestStatus.Code = common.OperationResp_OPERATION_SUCCESS
849 }
850 return err
khenaidoo43c82122018-11-22 18:38:28 -0500851}
852
Kent Hagerman2b216042020-04-03 18:28:56 -0400853func (agent *Agent) updateDeviceStatus(ctx context.Context, operStatus voltha.OperStatus_Types, connStatus voltha.ConnectStatus_Types) error {
khenaidood948f772021-08-11 17:49:24 -0400854 var err error
855 var desc string
856 opStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
857 defer func() { agent.logDeviceUpdate(ctx, nil, nil, opStatus, err, desc) }()
858
859 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
khenaidoo442e7c72020-03-10 16:13:48 -0400860 return err
861 }
khenaidoo6e55d9e2019-12-12 18:26:26 -0500862
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400863 cloned := agent.cloneDeviceWithoutLock()
npujar1d86a522019-11-14 17:11:16 +0530864 // Ensure the enums passed in are valid - they will be invalid if they are not set when this function is invoked
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400865 if s, ok := voltha.ConnectStatus_Types_name[int32(connStatus)]; ok {
khenaidood948f772021-08-11 17:49:24 -0400866 logger.Debugw(ctx, "update-device-conn-status", log.Fields{"ok": ok, "val": s})
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400867 cloned.ConnectStatus = connStatus
npujar1d86a522019-11-14 17:11:16 +0530868 }
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400869 if s, ok := voltha.OperStatus_Types_name[int32(operStatus)]; ok {
khenaidood948f772021-08-11 17:49:24 -0400870 logger.Debugw(ctx, "update-device-oper-status", log.Fields{"ok": ok, "val": s})
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400871 cloned.OperStatus = operStatus
npujar1d86a522019-11-14 17:11:16 +0530872 }
Himani Chawlab4c25912020-11-12 17:16:38 +0530873 logger.Debugw(ctx, "update-device-status", log.Fields{"device-id": cloned.Id, "oper-status": cloned.OperStatus, "connect-status": cloned.ConnectStatus})
npujar1d86a522019-11-14 17:11:16 +0530874 // Store the device
khenaidood948f772021-08-11 17:49:24 -0400875 if err = agent.updateDeviceAndReleaseLock(ctx, cloned); err == nil {
876 opStatus.Code = common.OperationResp_OPERATION_SUCCESS
877 }
878 return err
khenaidoo92e62c52018-10-03 14:02:54 -0400879}
880
khenaidoob9203542018-09-17 22:56:37 -0400881// TODO: A generic device update by attribute
Kent Hagerman2b216042020-04-03 18:28:56 -0400882func (agent *Agent) updateDeviceAttribute(ctx context.Context, name string, value interface{}) {
khenaidoob9203542018-09-17 22:56:37 -0400883 if value == nil {
884 return
885 }
khenaidoo6e55d9e2019-12-12 18:26:26 -0500886
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400887 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
888 logger.Warnw(ctx, "request-aborted", log.Fields{"device-id": agent.deviceID, "name": name, "error": err})
889 return
890 }
891
892 cloned := agent.cloneDeviceWithoutLock()
khenaidoob9203542018-09-17 22:56:37 -0400893 updated := false
khenaidoo6e55d9e2019-12-12 18:26:26 -0500894 s := reflect.ValueOf(cloned).Elem()
khenaidoob9203542018-09-17 22:56:37 -0400895 if s.Kind() == reflect.Struct {
896 // exported field
897 f := s.FieldByName(name)
898 if f.IsValid() && f.CanSet() {
899 switch f.Kind() {
900 case reflect.String:
901 f.SetString(value.(string))
902 updated = true
903 case reflect.Uint32:
904 f.SetUint(uint64(value.(uint32)))
905 updated = true
906 case reflect.Bool:
907 f.SetBool(value.(bool))
908 updated = true
909 }
910 }
911 }
divyadesaicb8b59d2020-08-18 09:55:47 +0000912 logger.Debugw(ctx, "update-field-status", log.Fields{"device-id": cloned.Id, "name": name, "updated": updated})
khenaidoob9203542018-09-17 22:56:37 -0400913 // Save the data
khenaidoo6e55d9e2019-12-12 18:26:26 -0500914
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400915 if err := agent.updateDeviceAndReleaseLock(ctx, cloned); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000916 logger.Warnw(ctx, "attribute-update-failed", log.Fields{"attribute": name, "value": value})
khenaidoob9203542018-09-17 22:56:37 -0400917 }
khenaidoob9203542018-09-17 22:56:37 -0400918}
serkant.uluderya334479d2019-04-10 08:26:15 -0700919
Kent Hagerman45a13e42020-04-13 12:23:50 -0400920func (agent *Agent) simulateAlarm(ctx context.Context, simulateReq *voltha.SimulateAlarmRequest) error {
khenaidood948f772021-08-11 17:49:24 -0400921 var err error
922 var desc string
923 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
924 defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
925
926 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
khenaidoo442e7c72020-03-10 16:13:48 -0400927 return err
928 }
929 defer agent.requestQueue.RequestComplete()
Himani Chawlab4c25912020-11-12 17:16:38 +0530930 logger.Debugw(ctx, "simulate-alarm", log.Fields{"device-id": agent.deviceID})
khenaidoo6e55d9e2019-12-12 18:26:26 -0500931
Kent Hagermancba2f302020-07-28 13:37:36 -0400932 device := agent.getDeviceReadOnlyWithoutLock()
khenaidoo6e55d9e2019-12-12 18:26:26 -0500933
khenaidood948f772021-08-11 17:49:24 -0400934 client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
khenaidoo442e7c72020-03-10 16:13:48 -0400935 if err != nil {
khenaidood948f772021-08-11 17:49:24 -0400936 logger.Errorw(ctx, "grpc-client-nil",
937 log.Fields{
938 "error": err,
939 "device-id": agent.deviceID,
940 "device-type": agent.deviceType,
941 "adapter-endpoint": device.AdapterEndpoint,
942 })
npujar1d86a522019-11-14 17:11:16 +0530943 return err
serkant.uluderya334479d2019-04-10 08:26:15 -0700944 }
khenaidood948f772021-08-11 17:49:24 -0400945 subCtx, cancel := context.WithTimeout(coreutils.WithAllMetadataFromContext(ctx), agent.rpcTimeout)
946 requestStatus.Code = common.OperationResp_OPERATION_IN_PROGRESS
947 go func() {
948 defer cancel()
khenaidoo9beaaf12021-10-19 17:32:01 -0400949 _, err := client.SimulateAlarm(subCtx, &ca.SimulateAlarmMessage{Device: device, Request: simulateReq})
khenaidood948f772021-08-11 17:49:24 -0400950 if err == nil {
951 agent.onSuccess(subCtx, nil, nil, false)
952 } else {
953 agent.onFailure(subCtx, err, nil, nil, false)
954 }
955 }()
serkant.uluderya334479d2019-04-10 08:26:15 -0700956 return nil
957}
Mahir Gunyelb5851672019-07-24 10:46:26 +0300958
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400959// This function updates the device in the DB, releases the device lock, and runs any state transitions.
960// The calling function MUST hold the device lock. The caller MUST NOT modify the device after this is called.
961func (agent *Agent) updateDeviceAndReleaseLock(ctx context.Context, device *voltha.Device) error {
962 // fail early if this agent is no longer valid
Kent Hagerman4f355f52020-03-30 16:01:33 -0400963 if agent.stopped {
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400964 agent.requestQueue.RequestComplete()
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530965 return errors.New("device-agent-stopped")
Thomas Lee Se5a44012019-11-07 20:32:24 +0530966 }
Kent Hagerman4f355f52020-03-30 16:01:33 -0400967
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400968 // update in db
Kent Hagermanf5a67352020-04-30 15:15:26 -0400969 if err := agent.dbProxy.Set(ctx, agent.deviceID, device); err != nil {
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400970 agent.requestQueue.RequestComplete()
Kent Hagerman4f355f52020-03-30 16:01:33 -0400971 return status.Errorf(codes.Internal, "failed-update-device:%s: %s", agent.deviceID, err)
Mahir Gunyelb5851672019-07-24 10:46:26 +0300972 }
divyadesaicb8b59d2020-08-18 09:55:47 +0000973 logger.Debugw(ctx, "updated-device-in-store", log.Fields{"device-id: ": agent.deviceID})
Mahir Gunyelb5851672019-07-24 10:46:26 +0300974
Kent Hagerman6031aad2020-07-29 16:36:33 -0400975 prevDevice := agent.device
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400976 // update the device
khenaidoo0db4c812020-05-27 15:27:30 -0400977 agent.device = device
Mahir Gunyelb0343bf2021-05-11 14:14:26 -0700978 //If any of the states has chenged, send the change event.
979 if prevDevice.OperStatus != device.OperStatus || prevDevice.ConnectStatus != device.ConnectStatus || prevDevice.AdminState != device.AdminState {
980 _ = agent.deviceMgr.Agent.SendDeviceStateChangeEvent(ctx, prevDevice.OperStatus, prevDevice.ConnectStatus, prevDevice.AdminState, device, time.Now().Unix())
981 }
Maninder0aabf0c2021-03-17 14:55:14 +0530982 deviceTransientState := agent.getTransientState()
983
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400984 // release lock before processing transition
985 agent.requestQueue.RequestComplete()
Himani Chawlab4c25912020-11-12 17:16:38 +0530986 subCtx := coreutils.WithSpanAndRPCMetadataFromContext(ctx)
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400987
Himani Chawlab4c25912020-11-12 17:16:38 +0530988 if err := agent.deviceMgr.stateTransitions.ProcessTransition(subCtx,
Maninder0aabf0c2021-03-17 14:55:14 +0530989 device, prevDevice, deviceTransientState, deviceTransientState); err != nil {
Himani Chawlab4c25912020-11-12 17:16:38 +0530990 logger.Errorw(ctx, "failed-process-transition", log.Fields{"device-id": device.Id, "previous-admin-state": prevDevice.AdminState, "current-admin-state": device.AdminState})
991 // Sending RPC EVENT here
992 rpce := agent.deviceMgr.NewRPCEvent(ctx, agent.deviceID, err.Error(), nil)
Himani Chawla606a4f02021-03-23 19:45:58 +0530993 agent.deviceMgr.SendRPCEvent(ctx, "RPC_ERROR_RAISE_EVENT", rpce, voltha.EventCategory_COMMUNICATION,
994 nil, time.Now().Unix())
Himani Chawlab4c25912020-11-12 17:16:38 +0530995
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400996 }
Mahir Gunyelb5851672019-07-24 10:46:26 +0300997 return nil
998}
Mahir Gunyelfdee9212019-10-16 16:52:21 -0700999
Himani Chawla2ba1c9c2020-10-07 13:19:03 +05301000// This function updates the device transient in the DB through loader, releases the device lock, and runs any state transitions.
1001// The calling function MUST hold the device lock. The caller MUST NOT modify the device after this is called.
1002func (agent *Agent) updateDeviceWithTransientStateAndReleaseLock(ctx context.Context, device *voltha.Device,
khenaidood948f772021-08-11 17:49:24 -04001003 transientState, prevTransientState core.DeviceTransientState_Types) error {
Himani Chawla2ba1c9c2020-10-07 13:19:03 +05301004 // fail early if this agent is no longer valid
1005 if agent.stopped {
1006 agent.requestQueue.RequestComplete()
1007 return errors.New("device-agent-stopped")
1008 }
1009 //update device TransientState
1010 if err := agent.updateTransientState(ctx, transientState); err != nil {
1011 agent.requestQueue.RequestComplete()
1012 return err
1013 }
1014 // update in db
1015 if err := agent.dbProxy.Set(ctx, agent.deviceID, device); err != nil {
1016 //Reverting TransientState update
khenaidood948f772021-08-11 17:49:24 -04001017 if errTransient := agent.updateTransientState(ctx, prevTransientState); errTransient != nil {
1018 logger.Errorw(ctx, "failed-to-revert-transient-state-update-on-error", log.Fields{"device-id": device.Id,
1019 "previous-transient-state": prevTransientState, "current-transient-state": transientState, "error": errTransient})
1020 }
Himani Chawla2ba1c9c2020-10-07 13:19:03 +05301021 agent.requestQueue.RequestComplete()
1022 return status.Errorf(codes.Internal, "failed-update-device:%s: %s", agent.deviceID, err)
1023 }
1024
1025 logger.Debugw(ctx, "updated-device-in-store", log.Fields{"device-id: ": agent.deviceID})
1026
1027 prevDevice := agent.device
1028 // update the device
1029 agent.device = device
Mahir Gunyelb0343bf2021-05-11 14:14:26 -07001030 //If any of the states has chenged, send the change event.
1031 if prevDevice.OperStatus != device.OperStatus || prevDevice.ConnectStatus != device.ConnectStatus || prevDevice.AdminState != device.AdminState {
1032 _ = agent.deviceMgr.Agent.SendDeviceStateChangeEvent(ctx, prevDevice.OperStatus, prevDevice.ConnectStatus, prevDevice.AdminState, device, time.Now().Unix())
1033 }
Himani Chawla2ba1c9c2020-10-07 13:19:03 +05301034
1035 // release lock before processing transition
1036 agent.requestQueue.RequestComplete()
khenaidood948f772021-08-11 17:49:24 -04001037 go func() {
1038 subCtx := coreutils.WithSpanAndRPCMetadataFromContext(ctx)
1039 if err := agent.deviceMgr.stateTransitions.ProcessTransition(subCtx,
1040 device, prevDevice, transientState, prevTransientState); err != nil {
1041 logger.Errorw(ctx, "failed-process-transition", log.Fields{"device-id": device.Id, "previous-admin-state": prevDevice.AdminState, "current-admin-state": device.AdminState})
1042 // Sending RPC EVENT here
1043 rpce := agent.deviceMgr.NewRPCEvent(ctx, agent.deviceID, err.Error(), nil)
1044 agent.deviceMgr.SendRPCEvent(ctx, "RPC_ERROR_RAISE_EVENT", rpce, voltha.EventCategory_COMMUNICATION,
1045 nil, time.Now().Unix())
1046 }
1047 }()
Himani Chawla2ba1c9c2020-10-07 13:19:03 +05301048 return nil
1049}
Kent Hagerman2b216042020-04-03 18:28:56 -04001050func (agent *Agent) updateDeviceReason(ctx context.Context, reason string) error {
khenaidood948f772021-08-11 17:49:24 -04001051 logger.Debugw(ctx, "update-device-reason", log.Fields{"device-id": agent.deviceID, "reason": reason})
1052
1053 var err error
1054 var desc string
1055 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
1056 defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
1057
1058 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
khenaidoo442e7c72020-03-10 16:13:48 -04001059 return err
1060 }
Maninder9a1bc0d2020-10-26 11:34:02 +05301061
Kent Hagermanf6db9f12020-07-22 17:16:19 -04001062 cloned := agent.cloneDeviceWithoutLock()
npujar1d86a522019-11-14 17:11:16 +05301063 cloned.Reason = reason
khenaidood948f772021-08-11 17:49:24 -04001064 if err = agent.updateDeviceAndReleaseLock(ctx, cloned); err == nil {
1065 requestStatus.Code = common.OperationResp_OPERATION_SUCCESS
Maninder9a1bc0d2020-10-26 11:34:02 +05301066 }
khenaidood948f772021-08-11 17:49:24 -04001067 return err
Mahir Gunyelfdee9212019-10-16 16:52:21 -07001068}
kesavandbc2d1622020-01-21 00:42:01 -05001069
Kent Hagerman2b216042020-04-03 18:28:56 -04001070func (agent *Agent) ChildDeviceLost(ctx context.Context, device *voltha.Device) error {
Himani Chawlab4c25912020-11-12 17:16:38 +05301071 logger.Debugw(ctx, "child-device-lost", log.Fields{"child-device-id": device.Id, "parent-device-id": agent.deviceID})
Chaitrashree G S543df3e2020-02-24 22:36:54 -05001072
khenaidood948f772021-08-11 17:49:24 -04001073 var err error
1074 var desc string
1075 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
1076 defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
1077
Kent Hagerman2a07b862020-06-19 15:23:07 -04001078 // Remove the associated peer ports on the parent device
1079 for portID := range agent.portLoader.ListIDs() {
1080 if portHandle, have := agent.portLoader.Lock(portID); have {
1081 oldPort := portHandle.GetReadOnly()
1082 updatedPeers := make([]*voltha.Port_PeerPort, 0)
1083 for _, peerPort := range oldPort.Peers {
1084 if peerPort.DeviceId != device.Id {
1085 updatedPeers = append(updatedPeers, peerPort)
1086 }
khenaidoo442e7c72020-03-10 16:13:48 -04001087 }
Kent Hagerman2a07b862020-06-19 15:23:07 -04001088 newPort := *oldPort
1089 newPort.Peers = updatedPeers
1090 if err := portHandle.Update(ctx, &newPort); err != nil {
1091 portHandle.Unlock()
1092 return nil
1093 }
1094 portHandle.Unlock()
khenaidoo442e7c72020-03-10 16:13:48 -04001095 }
Chaitrashree G S543df3e2020-02-24 22:36:54 -05001096 }
1097
khenaidoo442e7c72020-03-10 16:13:48 -04001098 //send request to adapter
khenaidood948f772021-08-11 17:49:24 -04001099 client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
khenaidoo442e7c72020-03-10 16:13:48 -04001100 if err != nil {
khenaidood948f772021-08-11 17:49:24 -04001101 logger.Errorw(ctx, "grpc-client-nil",
1102 log.Fields{
1103 "error": err,
1104 "device-id": agent.deviceID,
1105 "device-type": agent.deviceType,
1106 "adapter-endpoint": device.AdapterEndpoint,
1107 })
khenaidoo442e7c72020-03-10 16:13:48 -04001108 return err
Chaitrashree G S543df3e2020-02-24 22:36:54 -05001109 }
khenaidood948f772021-08-11 17:49:24 -04001110 subCtx, cancel := context.WithTimeout(coreutils.WithAllMetadataFromContext(ctx), agent.rpcTimeout)
1111 requestStatus.Code = common.OperationResp_OPERATION_IN_PROGRESS
1112 go func() {
1113 defer cancel()
1114 _, err := client.ChildDeviceLost(subCtx, device)
1115 if err == nil {
1116 agent.onSuccess(subCtx, nil, nil, true)
1117 } else {
1118 agent.onFailure(subCtx, err, nil, nil, true)
1119 }
1120 }()
Chaitrashree G S543df3e2020-02-24 22:36:54 -05001121 return nil
Chaitrashree G S543df3e2020-02-24 22:36:54 -05001122}
onkarkundargi87285252020-01-27 11:34:52 +05301123
khenaidoo9beaaf12021-10-19 17:32:01 -04001124func (agent *Agent) startOmciTest(ctx context.Context, omcitestrequest *omci.OmciTestRequest) (*omci.TestResponse, error) {
khenaidood948f772021-08-11 17:49:24 -04001125 var err error
1126 var desc string
1127 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
1128 defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
onkarkundargi87285252020-01-27 11:34:52 +05301129
khenaidood948f772021-08-11 17:49:24 -04001130 // OMCI test may be performed on a pre-provisioned device. If a device is in that state both its device type and endpoint
1131 // may not have been set yet.
1132 // First check if we need to update the type or endpoint
1133 cloned, err := agent.getDeviceReadOnly(ctx)
onkarkundargi87285252020-01-27 11:34:52 +05301134 if err != nil {
1135 return nil, err
1136 }
khenaidood948f772021-08-11 17:49:24 -04001137 if cloned.Type == "" || cloned.AdapterEndpoint == "" {
1138 if err = agent.updateDeviceTypeAndEndpoint(ctx); err != nil {
1139 return nil, err
1140 }
1141 cloned, err = agent.getDeviceReadOnly(ctx)
1142 if err != nil {
1143 return nil, err
1144 }
onkarkundargi87285252020-01-27 11:34:52 +05301145 }
1146
khenaidood948f772021-08-11 17:49:24 -04001147 // Send request to the adapter
1148 client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
1149 if err != nil {
1150 logger.Errorw(ctx, "grpc-client-nil",
1151 log.Fields{
1152 "error": err,
1153 "device-id": agent.deviceID,
1154 "device-type": agent.deviceType,
1155 "adapter-endpoint": cloned.AdapterEndpoint,
1156 })
1157 return nil, err
onkarkundargi87285252020-01-27 11:34:52 +05301158 }
khenaidood948f772021-08-11 17:49:24 -04001159
khenaidoo9beaaf12021-10-19 17:32:01 -04001160 res, err := client.StartOmciTest(ctx, &ca.OMCITest{
khenaidood948f772021-08-11 17:49:24 -04001161 Device: cloned,
1162 Request: omcitestrequest,
1163 })
1164 if err == nil {
1165 requestStatus.Code = common.OperationResp_OPERATION_SUCCESS
1166 }
1167 return res, err
onkarkundargi87285252020-01-27 11:34:52 +05301168}
Dinesh Belwalkarc1129f12020-02-27 10:41:33 -08001169
khenaidoo9beaaf12021-10-19 17:32:01 -04001170func (agent *Agent) getExtValue(ctx context.Context, pdevice *voltha.Device, cdevice *voltha.Device, valueparam *extension.ValueSpecifier) (*extension.ReturnValues, error) {
Himani Chawlab4c25912020-11-12 17:16:38 +05301171 logger.Debugw(ctx, "get-ext-value", log.Fields{"device-id": agent.deviceID, "onu-id": valueparam.Id, "value-type": valueparam.Value})
khenaidood948f772021-08-11 17:49:24 -04001172 var err error
1173 var desc string
1174 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
1175 defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
1176
1177 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
Dinesh Belwalkarc1129f12020-02-27 10:41:33 -08001178 return nil, err
1179 }
1180
khenaidood948f772021-08-11 17:49:24 -04001181 //send request to adapter synchronously
1182 client, err := agent.adapterMgr.GetAdapterClient(ctx, pdevice.AdapterEndpoint)
Dinesh Belwalkarc1129f12020-02-27 10:41:33 -08001183 if err != nil {
khenaidood948f772021-08-11 17:49:24 -04001184 logger.Errorw(ctx, "grpc-client-nil",
1185 log.Fields{
1186 "error": err,
1187 "device-id": agent.deviceID,
1188 "device-type": agent.deviceType,
1189 "adapter-endpoint": pdevice.AdapterEndpoint,
1190 })
1191 agent.requestQueue.RequestComplete()
Dinesh Belwalkarc1129f12020-02-27 10:41:33 -08001192 return nil, err
1193 }
1194
khenaidood948f772021-08-11 17:49:24 -04001195 // Release lock before sending to adapter
1196 agent.requestQueue.RequestComplete()
Dinesh Belwalkarc1129f12020-02-27 10:41:33 -08001197
khenaidoo9beaaf12021-10-19 17:32:01 -04001198 retVal, err := client.GetExtValue(ctx, &ca.GetExtValueMessage{
khenaidood948f772021-08-11 17:49:24 -04001199 ParentDevice: pdevice,
1200 ChildDevice: cdevice,
1201 ValueType: valueparam.Value,
1202 })
1203 if err == nil {
1204 requestStatus.Code = common.OperationResp_OPERATION_SUCCESS
Dinesh Belwalkarc1129f12020-02-27 10:41:33 -08001205 }
khenaidood948f772021-08-11 17:49:24 -04001206 return retVal, err
Dinesh Belwalkarc1129f12020-02-27 10:41:33 -08001207}
dpaul62686312020-06-23 14:17:36 +05301208
khenaidoo9beaaf12021-10-19 17:32:01 -04001209func (agent *Agent) setExtValue(ctx context.Context, device *voltha.Device, value *extension.ValueSet) (*empty.Empty, error) {
Himani Chawlab4c25912020-11-12 17:16:38 +05301210 logger.Debugw(ctx, "set-ext-value", log.Fields{"device-id": value.Id})
khenaidood948f772021-08-11 17:49:24 -04001211
1212 var err error
1213 var desc string
1214 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
1215 defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
1216
1217 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
dpaul62686312020-06-23 14:17:36 +05301218 return nil, err
1219 }
1220
1221 //send request to adapter
khenaidood948f772021-08-11 17:49:24 -04001222 //send request to adapter synchronously
1223 client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
dpaul62686312020-06-23 14:17:36 +05301224 if err != nil {
khenaidood948f772021-08-11 17:49:24 -04001225 logger.Errorw(ctx, "grpc-client-nil",
1226 log.Fields{
1227 "error": err,
1228 "device-id": agent.deviceID,
1229 "device-type": agent.deviceType,
1230 "adapter-endpoint": device.AdapterEndpoint,
1231 })
1232 agent.requestQueue.RequestComplete()
dpaul62686312020-06-23 14:17:36 +05301233 return nil, err
1234 }
khenaidood948f772021-08-11 17:49:24 -04001235 // Release lock before sending request to adapter
1236 agent.requestQueue.RequestComplete()
dpaul62686312020-06-23 14:17:36 +05301237
khenaidoo9beaaf12021-10-19 17:32:01 -04001238 retVal, err := client.SetExtValue(ctx, &ca.SetExtValueMessage{
khenaidood948f772021-08-11 17:49:24 -04001239 Device: device,
1240 Value: value,
1241 })
1242 if err == nil {
1243 requestStatus.Code = common.OperationResp_OPERATION_SUCCESS
dpaul62686312020-06-23 14:17:36 +05301244 }
khenaidood948f772021-08-11 17:49:24 -04001245 return retVal, err
dpaul62686312020-06-23 14:17:36 +05301246}
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301247
1248func (agent *Agent) getSingleValue(ctx context.Context, request *extension.SingleGetValueRequest) (*extension.SingleGetValueResponse, error) {
Himani Chawlab4c25912020-11-12 17:16:38 +05301249 logger.Debugw(ctx, "get-single-value", log.Fields{"device-id": request.TargetId})
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301250
khenaidood948f772021-08-11 17:49:24 -04001251 var err error
1252 var desc string
1253 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
1254 defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
1255
1256 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301257 return nil, err
1258 }
1259
1260 cloned := agent.cloneDeviceWithoutLock()
1261
1262 //send request to adapter
khenaidood948f772021-08-11 17:49:24 -04001263 client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301264 if err != nil {
khenaidood948f772021-08-11 17:49:24 -04001265 logger.Errorw(ctx, "grpc-client-nil",
1266 log.Fields{
1267 "error": err,
1268 "device-id": cloned.Id,
1269 "adapter-endpoint": cloned.AdapterEndpoint,
1270 })
1271 agent.requestQueue.RequestComplete()
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301272 return nil, err
1273 }
khenaidood948f772021-08-11 17:49:24 -04001274 // Release lock before sending request to adapter
1275 agent.requestQueue.RequestComplete()
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301276
khenaidood948f772021-08-11 17:49:24 -04001277 resp, err := client.GetSingleValue(ctx, request)
1278 if err == nil {
1279 requestStatus.Code = common.OperationResp_OPERATION_SUCCESS
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301280 }
khenaidood948f772021-08-11 17:49:24 -04001281 return resp, err
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301282}
1283
1284func (agent *Agent) setSingleValue(ctx context.Context, request *extension.SingleSetValueRequest) (*extension.SingleSetValueResponse, error) {
Himani Chawlab4c25912020-11-12 17:16:38 +05301285 logger.Debugw(ctx, "set-single-value", log.Fields{"device-id": request.TargetId})
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301286
khenaidood948f772021-08-11 17:49:24 -04001287 var err error
1288 var desc string
1289 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
1290 defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
1291
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301292 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
1293 return nil, err
1294 }
1295
1296 cloned := agent.cloneDeviceWithoutLock()
1297
1298 //send request to adapter
khenaidood948f772021-08-11 17:49:24 -04001299 client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301300 if err != nil {
khenaidood948f772021-08-11 17:49:24 -04001301 logger.Errorw(ctx, "grpc-client-nil",
1302 log.Fields{
1303 "error": err,
1304 "device-id": agent.deviceID,
1305 "device-type": agent.deviceType,
1306 "adapter-endpoint": cloned.AdapterEndpoint,
1307 })
1308 agent.requestQueue.RequestComplete()
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301309 return nil, err
1310 }
khenaidood948f772021-08-11 17:49:24 -04001311 // Release lock before sending request to adapter
1312 agent.requestQueue.RequestComplete()
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301313
khenaidood948f772021-08-11 17:49:24 -04001314 resp, err := client.SetSingleValue(ctx, request)
1315 if err == nil {
1316 requestStatus.Code = common.OperationResp_OPERATION_SUCCESS
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301317 }
khenaidood948f772021-08-11 17:49:24 -04001318 return resp, err
Salman Siddiqui1cf95042020-11-19 00:42:56 +05301319}
Maninder0aabf0c2021-03-17 14:55:14 +05301320
Maninder2195ccc2021-06-23 20:23:01 +05301321func (agent *Agent) proceedWithRequest(device *voltha.Device) bool {
1322 return !agent.isDeletionInProgress() && !agent.isInReconcileState(device)
Maninder0aabf0c2021-03-17 14:55:14 +05301323}
1324
1325func (agent *Agent) stopReconcile() {
1326 agent.stopReconcilingMutex.Lock()
1327 if agent.stopReconciling != nil {
1328 agent.stopReconciling <- 0
1329 }
1330 agent.stopReconcilingMutex.Unlock()
1331}
1332
khenaidood948f772021-08-11 17:49:24 -04001333// abortAllProcessing is invoked when an adapter managing this device is restarted
1334func (agent *Agent) abortAllProcessing(ctx context.Context) error {
1335 logger.Infow(ctx, "aborting-current-running-requests", log.Fields{"device-id": agent.deviceID})
1336 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
1337 return err
1338 }
1339 defer agent.requestQueue.RequestComplete()
1340
1341 // If any reconciling is in progress just abort it. The adapter is gone.
1342 agent.stopReconcile()
1343
1344 // Update the Core device transient state accordingly
1345 var updatedState core.DeviceTransientState_Types
1346 switch agent.getTransientState() {
1347 case core.DeviceTransientState_RECONCILE_IN_PROGRESS:
1348 updatedState = core.DeviceTransientState_NONE
1349 case core.DeviceTransientState_FORCE_DELETING:
1350 updatedState = core.DeviceTransientState_DELETE_FAILED
1351 case core.DeviceTransientState_DELETING_FROM_ADAPTER:
1352 updatedState = core.DeviceTransientState_DELETE_FAILED
1353 default:
1354 updatedState = core.DeviceTransientState_NONE
1355 }
1356 if err := agent.updateTransientState(ctx, updatedState); err != nil {
1357 logger.Errorf(ctx, "transient-state-update-failed", log.Fields{"error": err})
1358 return err
1359 }
1360 return nil
1361}
1362
1363func (agent *Agent) ReconcileDevice(ctx context.Context) {
1364 requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
Maninder0aabf0c2021-03-17 14:55:14 +05301365 var desc string
Maninder0aabf0c2021-03-17 14:55:14 +05301366
1367 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
khenaidood948f772021-08-11 17:49:24 -04001368 agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc)
1369 return
1370 }
1371
1372 device := agent.getDeviceReadOnlyWithoutLock()
1373 if device.AdminState == voltha.AdminState_PREPROVISIONED {
1374 agent.requestQueue.RequestComplete()
1375 logger.Debugw(ctx, "device-in-preprovisioning-state-reconcile-not-needed", log.Fields{"device-id": device.Id})
Maninder0aabf0c2021-03-17 14:55:14 +05301376 return
1377 }
1378
Maninder2195ccc2021-06-23 20:23:01 +05301379 if !agent.proceedWithRequest(device) {
Maninder0aabf0c2021-03-17 14:55:14 +05301380 agent.requestQueue.RequestComplete()
khenaidood948f772021-08-11 17:49:24 -04001381 err := fmt.Errorf("cannot complete operation as device deletion/reconciling is in progress or reconcile failed for device : %s", device.Id)
1382 logger.Errorw(ctx, "reconcile-failed", log.Fields{"error": err})
1383 agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc)
Maninder0aabf0c2021-03-17 14:55:14 +05301384 return
1385 }
1386
1387 //set transient state to RECONCILE IN PROGRESS
khenaidood948f772021-08-11 17:49:24 -04001388 err := agent.updateTransientState(ctx, core.DeviceTransientState_RECONCILE_IN_PROGRESS)
Maninder0aabf0c2021-03-17 14:55:14 +05301389 if err != nil {
1390 agent.requestQueue.RequestComplete()
khenaidood948f772021-08-11 17:49:24 -04001391 logger.Errorw(ctx, "setting-transient-state-failed", log.Fields{"error": err})
1392 agent.logDeviceUpdate(ctx, nil, nil, requestStatus, nil, desc)
Maninder0aabf0c2021-03-17 14:55:14 +05301393 return
1394 }
1395
Maninder0aabf0c2021-03-17 14:55:14 +05301396 reconcilingBackoff := backoff.NewExponentialBackOff()
1397 reconcilingBackoff.InitialInterval = agent.config.BackoffRetryInitialInterval
1398 reconcilingBackoff.MaxElapsedTime = agent.config.BackoffRetryMaxElapsedTime
1399 reconcilingBackoff.MaxInterval = agent.config.BackoffRetryMaxInterval
1400
1401 //making here to keep lifecycle of this channel within the scope of retryReconcile
1402 agent.stopReconcilingMutex.Lock()
1403 agent.stopReconciling = make(chan int)
1404 agent.stopReconcilingMutex.Unlock()
1405
David K. Bainbridge482e4422021-06-30 12:23:42 -07001406 // defined outside the retry loop so it can be cleaned
1407 // up when the loop breaks
1408 var backoffTimer *time.Timer
1409
1410retry:
Maninder0aabf0c2021-03-17 14:55:14 +05301411 for {
David K. Bainbridge482e4422021-06-30 12:23:42 -07001412 // If the operations state of the device is RECONCILING_FAILED then we do not
1413 // want to continue to attempt reconciliation.
1414 deviceRef := agent.getDeviceReadOnlyWithoutLock()
1415 if deviceRef.OperStatus == common.OperStatus_RECONCILING_FAILED {
1416 logger.Warnw(ctx, "reconciling-failed-halting-retries",
1417 log.Fields{"device-id": device.Id})
1418 agent.requestQueue.RequestComplete()
1419 break retry
1420 }
1421
Maninder0aabf0c2021-03-17 14:55:14 +05301422 // Use an exponential back off to prevent getting into a tight loop
1423 duration := reconcilingBackoff.NextBackOff()
1424 //This case should never occur in default case as max elapsed time for backoff is 0(by default) , so it will never return stop
1425 if duration == backoff.Stop {
1426 // If we reach a maximum then warn and reset the backoff
1427 // timer and keep attempting.
1428 logger.Warnw(ctx, "maximum-reconciling-backoff-reached--resetting-backoff-timer",
1429 log.Fields{"max-reconciling-backoff": reconcilingBackoff.MaxElapsedTime,
1430 "device-id": device.Id})
1431 reconcilingBackoff.Reset()
1432 duration = reconcilingBackoff.NextBackOff()
1433 }
1434
David K. Bainbridge482e4422021-06-30 12:23:42 -07001435 backoffTimer = time.NewTimer(duration)
Maninder0aabf0c2021-03-17 14:55:14 +05301436
khenaidood948f772021-08-11 17:49:24 -04001437 logger.Debugw(ctx, "retrying-reconciling", log.Fields{"deviceID": device.Id, "endpoint": device.AdapterEndpoint})
1438 // Release lock before sending request to adapter
Maninder0aabf0c2021-03-17 14:55:14 +05301439 agent.requestQueue.RequestComplete()
khenaidood948f772021-08-11 17:49:24 -04001440
1441 // Send a reconcile request to the adapter.
1442 err := agent.sendReconcileRequestToAdapter(ctx, device)
1443 if errors.Is(err, errContextExpired) || errors.Is(err, errReconcileAborted) {
1444 logger.Errorw(ctx, "reconcile-aborted", log.Fields{"error": err})
1445 requestStatus = &common.OperationResp{Code: common.OperationResp_OperationReturnCode(common.OperStatus_FAILED)}
1446 desc = "aborted"
1447 agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc)
1448 break retry
1449 }
Maninder0aabf0c2021-03-17 14:55:14 +05301450 if err != nil {
khenaidood948f772021-08-11 17:49:24 -04001451 agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc)
Maninder0aabf0c2021-03-17 14:55:14 +05301452 <-backoffTimer.C
1453 // backoffTimer expired continue
1454 // Take lock back before retrying
1455 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
khenaidood948f772021-08-11 17:49:24 -04001456 agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc)
David K. Bainbridge482e4422021-06-30 12:23:42 -07001457 break retry
Maninder0aabf0c2021-03-17 14:55:14 +05301458 }
1459 continue
1460 }
khenaidood948f772021-08-11 17:49:24 -04001461 // Success
1462 requestStatus = &common.OperationResp{Code: common.OperationResp_OPERATION_SUCCESS}
1463 desc = "adapter-response"
1464 agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc)
1465 break retry
David K. Bainbridge482e4422021-06-30 12:23:42 -07001466 }
1467
1468 // Retry loop is broken, so stop any timers and drain the channel
1469 if backoffTimer != nil && !backoffTimer.Stop() {
1470
1471 // As per documentation and stack overflow when a timer is stopped its
1472 // channel should be drained. The issue is that Stop returns false
1473 // either if the timer has already been fired "OR" if the timer can be
1474 // stopped before being fired. This means that in some cases the
1475 // channel has already be emptied so attempting to read from it means
1476 // a blocked thread. To get around this use a select so if the
1477 // channel is already empty the default case hits and we are not
1478 // blocked.
1479 select {
1480 case <-backoffTimer.C:
1481 default:
Maninder0aabf0c2021-03-17 14:55:14 +05301482 }
1483 }
1484}
1485
khenaidood948f772021-08-11 17:49:24 -04001486func (agent *Agent) sendReconcileRequestToAdapter(ctx context.Context, device *voltha.Device) error {
1487 logger.Debugw(ctx, "sending-reconcile-to-adapter", log.Fields{"device-id": device.Id, "endpoint": agent.adapterEndpoint})
1488 client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
1489 if err != nil {
1490 return err
1491 }
1492 adapterResponse := make(chan error)
1493 go func() {
1494 _, err := client.ReconcileDevice(ctx, device)
1495 adapterResponse <- err
1496 }()
Maninder0aabf0c2021-03-17 14:55:14 +05301497 select {
1498 // wait for response
khenaidood948f772021-08-11 17:49:24 -04001499 case err := <-adapterResponse:
1500 if err != nil {
1501 return err
Maninder0aabf0c2021-03-17 14:55:14 +05301502 }
Maninder0aabf0c2021-03-17 14:55:14 +05301503 //In case of success quit retrying and wait for adapter to reset operation state of device
1504 agent.stopReconcilingMutex.Lock()
1505 agent.stopReconciling = nil
1506 agent.stopReconcilingMutex.Unlock()
1507 return nil
1508
1509 //if reconciling need to be stopped
1510 case _, ok := <-agent.stopReconciling:
1511 agent.stopReconcilingMutex.Lock()
1512 agent.stopReconciling = nil
1513 agent.stopReconcilingMutex.Unlock()
1514 if !ok {
1515 //channel-closed
khenaidood948f772021-08-11 17:49:24 -04001516 return fmt.Errorf("reconcile channel closed:%w", errReconcileAborted)
Maninder0aabf0c2021-03-17 14:55:14 +05301517 }
khenaidood948f772021-08-11 17:49:24 -04001518 return fmt.Errorf("reconciling aborted:%w", errReconcileAborted)
1519 // Context expired
1520 case <-ctx.Done():
1521 return fmt.Errorf("context expired:%s :%w", ctx.Err(), errContextExpired)
Maninder0aabf0c2021-03-17 14:55:14 +05301522 }
Maninder0aabf0c2021-03-17 14:55:14 +05301523}
1524
1525func (agent *Agent) reconcilingCleanup(ctx context.Context) error {
1526 var desc string
khenaidood948f772021-08-11 17:49:24 -04001527 var err error
Maninder0aabf0c2021-03-17 14:55:14 +05301528 operStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
khenaidood948f772021-08-11 17:49:24 -04001529 defer func() { agent.logDeviceUpdate(ctx, nil, nil, operStatus, err, desc) }()
1530
1531 if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
1532 desc = "reconcile-cleanup-failed"
Maninder0aabf0c2021-03-17 14:55:14 +05301533 return err
1534 }
1535 defer agent.requestQueue.RequestComplete()
khenaidood948f772021-08-11 17:49:24 -04001536 err = agent.updateTransientState(ctx, core.DeviceTransientState_NONE)
Maninder0aabf0c2021-03-17 14:55:14 +05301537 if err != nil {
khenaidood948f772021-08-11 17:49:24 -04001538 logger.Errorf(ctx, "transient-state-update-failed", log.Fields{"error": err})
Maninder0aabf0c2021-03-17 14:55:14 +05301539 return err
1540 }
khenaidood948f772021-08-11 17:49:24 -04001541 operStatus.Code = common.OperationResp_OPERATION_SUCCESS
Maninder0aabf0c2021-03-17 14:55:14 +05301542 return nil
1543}
khenaidood948f772021-08-11 17:49:24 -04001544
1545func (agent *Agent) isAdapterConnectionUp(ctx context.Context) bool {
1546 c, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
1547 return c != nil && err == nil
1548}
1549
1550func (agent *Agent) canDeviceRequestProceed(ctx context.Context) error {
1551 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
1552 return err
1553 }
1554 defer agent.requestQueue.RequestComplete()
1555 if agent.proceedWithRequest(agent.device) {
1556 return nil
1557 }
1558 return fmt.Errorf("device-cannot-process-request-%s", agent.deviceID)
1559}