blob: a9767d4bfde2df2b17fa840d71ec8ebab06042ff [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 Gunyeladdb66a2020-04-29 18:08:50 -070024 "reflect"
25 "sync"
26 "time"
27
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053028 "github.com/gogo/protobuf/proto"
khenaidoo442e7c72020-03-10 16:13:48 -040029 "github.com/golang/protobuf/ptypes"
dpaul62686312020-06-23 14:17:36 +053030 "github.com/golang/protobuf/ptypes/empty"
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053031 "github.com/opencord/voltha-go/db/model"
Kent Hagerman2b216042020-04-03 18:28:56 -040032 "github.com/opencord/voltha-go/rw_core/core/adapter"
Mahir Gunyel03de0d32020-06-03 01:36:59 -070033 "github.com/opencord/voltha-go/rw_core/core/device/flow"
34 "github.com/opencord/voltha-go/rw_core/core/device/group"
Kent Hagerman2a07b862020-06-19 15:23:07 -040035 "github.com/opencord/voltha-go/rw_core/core/device/port"
Kent Hagerman2b216042020-04-03 18:28:56 -040036 "github.com/opencord/voltha-go/rw_core/core/device/remote"
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053037 "github.com/opencord/voltha-go/rw_core/core/device/transientstate"
Scott Bakerb671a862019-10-24 10:53:40 -070038 coreutils "github.com/opencord/voltha-go/rw_core/utils"
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053039 "github.com/opencord/voltha-lib-go/v4/pkg/kafka"
Maninderdfadc982020-10-28 14:04:33 +053040 "github.com/opencord/voltha-lib-go/v4/pkg/log"
41 ic "github.com/opencord/voltha-protos/v4/go/inter_container"
42 ofp "github.com/opencord/voltha-protos/v4/go/openflow_13"
43 "github.com/opencord/voltha-protos/v4/go/voltha"
khenaidoob9203542018-09-17 22:56:37 -040044 "google.golang.org/grpc/codes"
45 "google.golang.org/grpc/status"
khenaidoob9203542018-09-17 22:56:37 -040046)
47
Kent Hagerman2b216042020-04-03 18:28:56 -040048// Agent represents device agent attributes
49type Agent struct {
Kent Hagermanf5a67352020-04-30 15:15:26 -040050 deviceID string
51 parentID string
52 deviceType string
Kent Hagerman2a07b862020-06-19 15:23:07 -040053 isRootDevice bool
Kent Hagermanf5a67352020-04-30 15:15:26 -040054 adapterProxy *remote.AdapterProxy
55 adapterMgr *adapter.Manager
56 deviceMgr *Manager
57 dbProxy *model.Proxy
58 exitChannel chan int
59 device *voltha.Device
60 requestQueue *coreutils.RequestQueue
61 defaultTimeout time.Duration
62 startOnce sync.Once
63 stopOnce sync.Once
64 stopped bool
Mahir Gunyel03de0d32020-06-03 01:36:59 -070065
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053066 flowLoader *flow.Loader
67 groupLoader *group.Loader
68 portLoader *port.Loader
69 transientStateLoader *transientstate.Loader
khenaidoob9203542018-09-17 22:56:37 -040070}
71
Kent Hagerman2b216042020-04-03 18:28:56 -040072//newAgent creates a new device agent. The device will be initialized when start() is called.
Kent Hagerman2a07b862020-06-19 15:23:07 -040073func newAgent(ap *remote.AdapterProxy, device *voltha.Device, deviceMgr *Manager, dbPath *model.Path, deviceProxy *model.Proxy, timeout time.Duration) *Agent {
74 deviceID := device.Id
75 if deviceID == "" {
76 deviceID = coreutils.CreateDeviceID()
Stephane Barbarie1ab43272018-12-08 21:42:13 -050077 }
Scott Baker80678602019-11-14 16:57:36 -080078
Kent Hagerman2a07b862020-06-19 15:23:07 -040079 return &Agent{
Himani Chawla2ba1c9c2020-10-07 13:19:03 +053080 deviceID: deviceID,
81 adapterProxy: ap,
82 isRootDevice: device.Root,
83 parentID: device.ParentId,
84 deviceType: device.Type,
85 deviceMgr: deviceMgr,
86 adapterMgr: deviceMgr.adapterMgr,
87 exitChannel: make(chan int, 1),
88 dbProxy: deviceProxy,
89 defaultTimeout: timeout,
90 device: proto.Clone(device).(*voltha.Device),
91 requestQueue: coreutils.NewRequestQueue(),
92 flowLoader: flow.NewLoader(dbPath.SubPath("flows").Proxy(deviceID)),
93 groupLoader: group.NewLoader(dbPath.SubPath("groups").Proxy(deviceID)),
94 portLoader: port.NewLoader(dbPath.SubPath("ports").Proxy(deviceID)),
95 transientStateLoader: transientstate.NewLoader(dbPath.SubPath("core").Proxy("transientstate"), deviceID),
Kent Hagerman2a07b862020-06-19 15:23:07 -040096 }
khenaidoob9203542018-09-17 22:56:37 -040097}
98
khenaidoo442e7c72020-03-10 16:13:48 -040099// start() saves the device to the data model and registers for callbacks on that device if deviceToCreate!=nil.
100// 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 -0800101// was started.
Kent Hagerman2b216042020-04-03 18:28:56 -0400102func (agent *Agent) start(ctx context.Context, deviceToCreate *voltha.Device) (*voltha.Device, error) {
khenaidoo442e7c72020-03-10 16:13:48 -0400103 needToStart := false
104 if agent.startOnce.Do(func() { needToStart = true }); !needToStart {
Kent Hagermancba2f302020-07-28 13:37:36 -0400105 return agent.getDeviceReadOnly(ctx)
khenaidoo442e7c72020-03-10 16:13:48 -0400106 }
107 var startSucceeded bool
108 defer func() {
109 if !startSucceeded {
110 if err := agent.stop(ctx); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000111 logger.Errorw(ctx, "failed-to-cleanup-after-unsuccessful-start", log.Fields{"device-id": agent.deviceID, "error": err})
khenaidoo442e7c72020-03-10 16:13:48 -0400112 }
113 }
114 }()
Scott Baker80678602019-11-14 16:57:36 -0800115
khenaidoo442e7c72020-03-10 16:13:48 -0400116 var device *voltha.Device
Scott Baker80678602019-11-14 16:57:36 -0800117 if deviceToCreate == nil {
118 // Load the existing device
Kent Hagerman4f355f52020-03-30 16:01:33 -0400119 device := &voltha.Device{}
Kent Hagermanf5a67352020-04-30 15:15:26 -0400120 have, err := agent.dbProxy.Get(ctx, agent.deviceID, device)
Thomas Lee Se5a44012019-11-07 20:32:24 +0530121 if err != nil {
Thomas Lee Se5a44012019-11-07 20:32:24 +0530122 return nil, err
Kent Hagerman4f355f52020-03-30 16:01:33 -0400123 } else if !have {
124 return nil, status.Errorf(codes.NotFound, "device-%s", agent.deviceID)
Thomas Lee Se5a44012019-11-07 20:32:24 +0530125 }
Kent Hagerman4f355f52020-03-30 16:01:33 -0400126
127 agent.deviceType = device.Adapter
128 agent.device = proto.Clone(device).(*voltha.Device)
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700129 // load the flows and groups from KV to cache
130 agent.flowLoader.Load(ctx)
131 agent.groupLoader.Load(ctx)
Kent Hagerman2a07b862020-06-19 15:23:07 -0400132 agent.portLoader.Load(ctx)
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530133 agent.transientStateLoader.Load(ctx)
Kent Hagerman4f355f52020-03-30 16:01:33 -0400134
Rohan Agrawal31f21802020-06-12 05:38:46 +0000135 logger.Infow(ctx, "device-loaded-from-dB", log.Fields{"device-id": agent.deviceID})
khenaidoo297cd252019-02-07 22:10:23 -0500136 } else {
Scott Baker80678602019-11-14 16:57:36 -0800137 // Create a new device
Kent Hagermanf5a67352020-04-30 15:15:26 -0400138 // Assumption is that AdminState, FlowGroups, and Flows are uninitialized since this
139 // is a new device, so populate them here before passing the device to ldProxy.Set.
Kent Hagerman2b216042020-04-03 18:28:56 -0400140 // agent.deviceId will also have been set during newAgent().
Scott Baker80678602019-11-14 16:57:36 -0800141 device = (proto.Clone(deviceToCreate)).(*voltha.Device)
npujar1d86a522019-11-14 17:11:16 +0530142 device.Id = agent.deviceID
Scott Baker80678602019-11-14 16:57:36 -0800143 device.AdminState = voltha.AdminState_PREPROVISIONED
Scott Baker80678602019-11-14 16:57:36 -0800144 if !deviceToCreate.GetRoot() && deviceToCreate.ProxyAddress != nil {
145 // Set the default vlan ID to the one specified by the parent adapter. It can be
146 // overwritten by the child adapter during a device update request
147 device.Vlan = deviceToCreate.ProxyAddress.ChannelId
148 }
149
khenaidoo297cd252019-02-07 22:10:23 -0500150 // Add the initial device to the local model
Kent Hagermanf5a67352020-04-30 15:15:26 -0400151 if err := agent.dbProxy.Set(ctx, agent.deviceID, device); err != nil {
Kent Hagerman4f355f52020-03-30 16:01:33 -0400152 return nil, status.Errorf(codes.Aborted, "failed-adding-device-%s: %s", agent.deviceID, err)
khenaidoo297cd252019-02-07 22:10:23 -0500153 }
khenaidoo442e7c72020-03-10 16:13:48 -0400154 agent.device = device
khenaidoob9203542018-09-17 22:56:37 -0400155 }
khenaidoo442e7c72020-03-10 16:13:48 -0400156 startSucceeded = true
Rohan Agrawalcf12f202020-08-03 04:42:01 +0000157 log.EnrichSpan(ctx, log.Fields{"device-id": agent.deviceID})
Rohan Agrawal31f21802020-06-12 05:38:46 +0000158 logger.Debugw(ctx, "device-agent-started", log.Fields{"device-id": agent.deviceID})
khenaidoo442e7c72020-03-10 16:13:48 -0400159
Kent Hagermancba2f302020-07-28 13:37:36 -0400160 return agent.getDeviceReadOnly(ctx)
khenaidoob9203542018-09-17 22:56:37 -0400161}
162
khenaidoo4d4802d2018-10-04 21:59:49 -0400163// stop stops the device agent. Not much to do for now
Kent Hagerman2b216042020-04-03 18:28:56 -0400164func (agent *Agent) stop(ctx context.Context) error {
khenaidoo442e7c72020-03-10 16:13:48 -0400165 needToStop := false
166 if agent.stopOnce.Do(func() { needToStop = true }); !needToStop {
167 return nil
168 }
169 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
170 return err
171 }
172 defer agent.requestQueue.RequestComplete()
khenaidoo49085352020-01-13 19:15:43 -0500173
divyadesaicb8b59d2020-08-18 09:55:47 +0000174 logger.Infow(ctx, "stopping-device-agent", log.Fields{"device-id": agent.deviceID, "parentId": agent.parentID})
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530175 // Remove the device transient loader
176 if err := agent.deleteTransientState(ctx); err != nil {
177 return err
178 }
khenaidoo0a822f92019-05-08 15:15:57 -0400179 // Remove the device from the KV store
Kent Hagermanf5a67352020-04-30 15:15:26 -0400180 if err := agent.dbProxy.Remove(ctx, agent.deviceID); err != nil {
khenaidoo442e7c72020-03-10 16:13:48 -0400181 return err
Thomas Lee Se5a44012019-11-07 20:32:24 +0530182 }
khenaidoo442e7c72020-03-10 16:13:48 -0400183
khenaidoo442e7c72020-03-10 16:13:48 -0400184 close(agent.exitChannel)
185
186 agent.stopped = true
187
Rohan Agrawal31f21802020-06-12 05:38:46 +0000188 logger.Infow(ctx, "device-agent-stopped", log.Fields{"device-id": agent.deviceID, "parent-id": agent.parentID})
khenaidoo442e7c72020-03-10 16:13:48 -0400189
190 return nil
khenaidoob9203542018-09-17 22:56:37 -0400191}
192
Scott Baker80678602019-11-14 16:57:36 -0800193// Load the most recent state from the KVStore for the device.
Kent Hagerman2b216042020-04-03 18:28:56 -0400194func (agent *Agent) reconcileWithKVStore(ctx context.Context) {
khenaidoo442e7c72020-03-10 16:13:48 -0400195 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000196 logger.Warnw(ctx, "request-aborted", log.Fields{"device-id": agent.deviceID, "error": err})
khenaidoo442e7c72020-03-10 16:13:48 -0400197 return
198 }
199 defer agent.requestQueue.RequestComplete()
Rohan Agrawal31f21802020-06-12 05:38:46 +0000200 logger.Debug(ctx, "reconciling-device-agent-devicetype")
Scott Baker80678602019-11-14 16:57:36 -0800201 // TODO: context timeout
Kent Hagerman4f355f52020-03-30 16:01:33 -0400202 device := &voltha.Device{}
Kent Hagermanf5a67352020-04-30 15:15:26 -0400203 if have, err := agent.dbProxy.Get(ctx, agent.deviceID, device); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000204 logger.Errorw(ctx, "kv-get-failed", log.Fields{"device-id": agent.deviceID, "error": err})
Thomas Lee Se5a44012019-11-07 20:32:24 +0530205 return
Kent Hagerman4f355f52020-03-30 16:01:33 -0400206 } else if !have {
207 return // not found in kv
Thomas Lee Se5a44012019-11-07 20:32:24 +0530208 }
Kent Hagerman4f355f52020-03-30 16:01:33 -0400209
210 agent.deviceType = device.Adapter
211 agent.device = device
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700212 agent.flowLoader.Load(ctx)
213 agent.groupLoader.Load(ctx)
Kent Hagerman2a07b862020-06-19 15:23:07 -0400214 agent.portLoader.Load(ctx)
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530215 agent.transientStateLoader.Load(ctx)
216
Rohan Agrawal31f21802020-06-12 05:38:46 +0000217 logger.Debugw(ctx, "reconciled-device-agent-devicetype", log.Fields{"device-id": agent.deviceID, "type": agent.deviceType})
Scott Baker80678602019-11-14 16:57:36 -0800218}
219
khenaidoo442e7c72020-03-10 16:13:48 -0400220// onSuccess is a common callback for scenarios where we receive a nil response following a request to an adapter
221// and the only action required is to publish a successful result on kafka
Rohan Agrawal31f21802020-06-12 05:38:46 +0000222func (agent *Agent) onSuccess(ctx context.Context, rpc string, response interface{}, reqArgs ...interface{}) {
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530223 logger.Debugw(ctx, "response-successful", log.Fields{"rpc": rpc, "device-id": agent.deviceID})
khenaidoo442e7c72020-03-10 16:13:48 -0400224 // TODO: Post success message onto kafka
225}
226
227// onFailure is a common callback for scenarios where we receive an error response following a request to an adapter
228// and the only action required is to publish the failed result on kafka
Rohan Agrawal31f21802020-06-12 05:38:46 +0000229func (agent *Agent) onFailure(ctx context.Context, rpc string, response interface{}, reqArgs ...interface{}) {
khenaidoo442e7c72020-03-10 16:13:48 -0400230 if res, ok := response.(error); ok {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000231 logger.Errorw(ctx, "rpc-failed", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "error": res, "args": reqArgs})
khenaidoo442e7c72020-03-10 16:13:48 -0400232 } else {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000233 logger.Errorw(ctx, "rpc-failed-invalid-error", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "args": reqArgs})
khenaidoo442e7c72020-03-10 16:13:48 -0400234 }
235 // TODO: Post failure message onto kafka
236}
237
Kent Hagerman2b216042020-04-03 18:28:56 -0400238func (agent *Agent) waitForAdapterResponse(ctx context.Context, cancel context.CancelFunc, rpc string, ch chan *kafka.RpcResponse,
khenaidoo442e7c72020-03-10 16:13:48 -0400239 onSuccess coreutils.ResponseCallback, onFailure coreutils.ResponseCallback, reqArgs ...interface{}) {
240 defer cancel()
241 select {
242 case rpcResponse, ok := <-ch:
243 if !ok {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000244 onFailure(ctx, rpc, status.Errorf(codes.Aborted, "channel-closed"), reqArgs)
khenaidoo442e7c72020-03-10 16:13:48 -0400245 } else if rpcResponse.Err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000246 onFailure(ctx, rpc, rpcResponse.Err, reqArgs)
khenaidoo442e7c72020-03-10 16:13:48 -0400247 } else {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000248 onSuccess(ctx, rpc, rpcResponse.Reply, reqArgs)
khenaidoo442e7c72020-03-10 16:13:48 -0400249 }
250 case <-ctx.Done():
Rohan Agrawal31f21802020-06-12 05:38:46 +0000251 onFailure(ctx, rpc, ctx.Err(), reqArgs)
khenaidoo442e7c72020-03-10 16:13:48 -0400252 }
253}
254
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530255// onDeleteSuccess is a common callback for scenarios where we receive a nil response following a delete request
256// to an adapter.
257func (agent *Agent) onDeleteSuccess(ctx context.Context, rpc string, response interface{}, reqArgs ...interface{}) {
258 logger.Debugw(ctx, "response-successful", log.Fields{"rpc": rpc, "device-id": agent.deviceID})
259 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
260 logger.Errorw(ctx, "delete-device-failure", log.Fields{"device-id": agent.deviceID, "error": err, "args": reqArgs})
261 }
262 previousDeviceTransientState := agent.getTransientState()
263 newDevice := agent.cloneDeviceWithoutLock()
264 if err := agent.updateDeviceWithTransientStateAndReleaseLock(ctx, newDevice,
265 voltha.DeviceTransientState_DELETING_POST_ADAPTER_RESPONSE, previousDeviceTransientState); err != nil {
266 logger.Errorw(ctx, "delete-device-failure", log.Fields{"device-id": agent.deviceID, "error": err, "args": reqArgs})
267 }
268}
269
270// onDeleteFailure is a common callback for scenarios where we receive an error response following a delete request
271// to an adapter and the only action required is to return the error response.
272func (agent *Agent) onDeleteFailure(ctx context.Context, rpc string, response interface{}, reqArgs ...interface{}) {
273 if res, ok := response.(error); ok {
274 logger.Errorw(ctx, "rpc-failed", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "error": res, "args": reqArgs})
275 } else {
276 logger.Errorw(ctx, "rpc-failed-invalid-error", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "args": reqArgs})
277 }
278 //Only updating of transient state is required, no transition.
279 if err := agent.updateTransientState(ctx, voltha.DeviceTransientState_DELETE_FAILED); err != nil {
280 logger.Errorw(ctx, "failed-to-update-transient-state-as-delete-failed", log.Fields{"device-id": agent.deviceID})
281 }
282
283}
284
285func (agent *Agent) waitForAdapterDeleteResponse(ctx context.Context, cancel context.CancelFunc, rpc string, ch chan *kafka.RpcResponse,
286 onSuccess coreutils.ResponseCallback, onFailure coreutils.ResponseCallback, reqArgs ...interface{}) {
287 defer cancel()
288 select {
289 case rpcResponse, ok := <-ch:
290 if !ok {
291 onFailure(ctx, rpc, status.Errorf(codes.Aborted, "channel-closed"), reqArgs)
292 } else if rpcResponse.Err != nil {
293 onFailure(ctx, rpc, rpcResponse.Err, reqArgs)
294 } else {
295 onSuccess(ctx, rpc, rpcResponse.Reply, reqArgs)
296 }
297 case <-ctx.Done():
298 onFailure(ctx, rpc, ctx.Err(), reqArgs)
299 }
300}
301
Kent Hagermancba2f302020-07-28 13:37:36 -0400302// getDeviceReadOnly returns a device which MUST NOT be modified, but is safe to keep forever.
303func (agent *Agent) getDeviceReadOnly(ctx context.Context) (*voltha.Device, error) {
khenaidoo442e7c72020-03-10 16:13:48 -0400304 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
305 return nil, err
306 }
307 defer agent.requestQueue.RequestComplete()
Kent Hagermancba2f302020-07-28 13:37:36 -0400308 return agent.device, nil
khenaidoo92e62c52018-10-03 14:02:54 -0400309}
310
Kent Hagermancba2f302020-07-28 13:37:36 -0400311// 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 -0400312// The device lock MUST be held by the caller.
Kent Hagermancba2f302020-07-28 13:37:36 -0400313func (agent *Agent) getDeviceReadOnlyWithoutLock() *voltha.Device {
khenaidoo0db4c812020-05-27 15:27:30 -0400314 return agent.device
khenaidoo92e62c52018-10-03 14:02:54 -0400315}
316
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400317// cloneDeviceWithoutLock returns a copy of the device which is safe to modify.
318// The device lock MUST be held by the caller.
319func (agent *Agent) cloneDeviceWithoutLock() *voltha.Device {
320 return proto.Clone(agent.device).(*voltha.Device)
321}
322
khenaidoo3ab34882019-05-02 21:33:30 -0400323// enableDevice activates a preprovisioned or a disable device
Kent Hagerman2b216042020-04-03 18:28:56 -0400324func (agent *Agent) enableDevice(ctx context.Context) error {
khenaidoo442e7c72020-03-10 16:13:48 -0400325 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
326 return err
327 }
Rohan Agrawal31f21802020-06-12 05:38:46 +0000328 logger.Debugw(ctx, "enableDevice", log.Fields{"device-id": agent.deviceID})
khenaidoo21d51152019-02-01 13:48:37 -0500329
Kent Hagermancba2f302020-07-28 13:37:36 -0400330 oldDevice := agent.getDeviceReadOnlyWithoutLock()
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400331 if oldDevice.AdminState == voltha.AdminState_ENABLED {
332 logger.Warnw(ctx, "device-already-enabled", log.Fields{"device-id": agent.deviceID})
333 agent.requestQueue.RequestComplete()
334 return status.Error(codes.FailedPrecondition, fmt.Sprintf("cannot-enable-an-already-enabled-device: %s", oldDevice.Id))
335 }
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530336 if agent.isDeletionInProgress() {
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400337 agent.requestQueue.RequestComplete()
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530338 return status.Errorf(codes.FailedPrecondition, "deviceId:%s, Device deletion is in progress.", agent.deviceID)
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400339 }
npujar1d86a522019-11-14 17:11:16 +0530340 // First figure out which adapter will handle this device type. We do it at this stage as allow devices to be
khenaidoo442e7c72020-03-10 16:13:48 -0400341 // pre-provisioned with the required adapter not registered. At this stage, since we need to communicate
npujar1d86a522019-11-14 17:11:16 +0530342 // with the adapter then we need to know the adapter that will handle this request
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400343 adapterName, err := agent.adapterMgr.GetAdapterType(oldDevice.Type)
npujar1d86a522019-11-14 17:11:16 +0530344 if err != nil {
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400345 agent.requestQueue.RequestComplete()
Matteo Scandolod525ae32020-04-02 17:27:29 -0700346 return err
npujar1d86a522019-11-14 17:11:16 +0530347 }
348
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400349 newDevice := agent.cloneDeviceWithoutLock()
350 newDevice.Adapter = adapterName
npujar1d86a522019-11-14 17:11:16 +0530351
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400352 // Update the Admin State and set the operational state to activating before sending the request to the Adapters
353 newDevice.AdminState = voltha.AdminState_ENABLED
354 newDevice.OperStatus = voltha.OperStatus_ACTIVATING
355 if err := agent.updateDeviceAndReleaseLock(ctx, newDevice); err != nil {
npujar1d86a522019-11-14 17:11:16 +0530356 return err
357 }
358
khenaidoo442e7c72020-03-10 16:13:48 -0400359 // Adopt the device if it was in pre-provision state. In all other cases, try to re-enable it.
khenaidoo442e7c72020-03-10 16:13:48 -0400360 var ch chan *kafka.RpcResponse
Rohan Agrawalcf12f202020-08-03 04:42:01 +0000361 subCtx, cancel := context.WithTimeout(log.WithSpanFromContext(context.Background(), ctx), agent.defaultTimeout)
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400362 if oldDevice.AdminState == voltha.AdminState_PREPROVISIONED {
363 ch, err = agent.adapterProxy.AdoptDevice(subCtx, newDevice)
khenaidoob9203542018-09-17 22:56:37 -0400364 } else {
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400365 ch, err = agent.adapterProxy.ReEnableDevice(subCtx, newDevice)
khenaidoob9203542018-09-17 22:56:37 -0400366 }
khenaidoo442e7c72020-03-10 16:13:48 -0400367 if err != nil {
368 cancel()
369 return err
370 }
371 // Wait for response
372 go agent.waitForAdapterResponse(subCtx, cancel, "enableDevice", ch, agent.onSuccess, agent.onFailure)
khenaidoob9203542018-09-17 22:56:37 -0400373 return nil
374}
375
Kent Hagerman2b216042020-04-03 18:28:56 -0400376func (agent *Agent) waitForAdapterFlowResponse(ctx context.Context, cancel context.CancelFunc, ch chan *kafka.RpcResponse, response coreutils.Response) {
khenaidoo442e7c72020-03-10 16:13:48 -0400377 defer cancel()
378 select {
379 case rpcResponse, ok := <-ch:
380 if !ok {
381 response.Error(status.Errorf(codes.Aborted, "channel-closed"))
382 } else if rpcResponse.Err != nil {
383 response.Error(rpcResponse.Err)
384 } else {
385 response.Done()
386 }
387 case <-ctx.Done():
388 response.Error(ctx.Err())
khenaidoo2c6a0992019-04-29 13:46:56 -0400389 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400390}
391
A R Karthick5c28f552019-12-11 22:47:44 -0800392//addFlowsAndGroups adds the "newFlows" and "newGroups" from the existing flows/groups and sends the update to the
393//adapters
Kent Hagerman2b216042020-04-03 18:28:56 -0400394func (agent *Agent) addFlowsAndGroups(ctx context.Context, newFlows []*ofp.OfpFlowStats, newGroups []*ofp.OfpGroupEntry, flowMetadata *voltha.FlowMetadata) error {
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700395 var flwResponse, grpResponse coreutils.Response
396 var err error
397 //if new flow list is empty then the called function returns quickly
398 if flwResponse, err = agent.addFlowsToAdapter(ctx, newFlows, flowMetadata); err != nil {
A R Karthick5c28f552019-12-11 22:47:44 -0800399 return err
400 }
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700401 //if new group list is empty then the called function returns quickly
402 if grpResponse, err = agent.addGroupsToAdapter(ctx, newGroups, flowMetadata); err != nil {
403 return err
404 }
405 if errs := coreutils.WaitForNilOrErrorResponses(agent.defaultTimeout, flwResponse, grpResponse); errs != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000406 logger.Warnw(ctx, "no-adapter-response", log.Fields{"device-id": agent.deviceID, "result": errs})
khenaidoo442e7c72020-03-10 16:13:48 -0400407 return status.Errorf(codes.Aborted, "flow-failure-device-%s", agent.deviceID)
khenaidoo0458db62019-06-20 08:50:36 -0400408 }
khenaidoo0458db62019-06-20 08:50:36 -0400409 return nil
410}
411
A R Karthick5c28f552019-12-11 22:47:44 -0800412//deleteFlowsAndGroups removes the "flowsToDel" and "groupsToDel" from the existing flows/groups and sends the update to the
413//adapters
Kent Hagerman2b216042020-04-03 18:28:56 -0400414func (agent *Agent) deleteFlowsAndGroups(ctx context.Context, flowsToDel []*ofp.OfpFlowStats, groupsToDel []*ofp.OfpGroupEntry, flowMetadata *voltha.FlowMetadata) error {
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700415 var flwResponse, grpResponse coreutils.Response
416 var err error
417 if flwResponse, err = agent.deleteFlowsFromAdapter(ctx, flowsToDel, flowMetadata); err != nil {
A R Karthick5c28f552019-12-11 22:47:44 -0800418 return err
419 }
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700420 if grpResponse, err = agent.deleteGroupsFromAdapter(ctx, groupsToDel, flowMetadata); err != nil {
421 return err
422 }
423
424 if res := coreutils.WaitForNilOrErrorResponses(agent.defaultTimeout, flwResponse, grpResponse); res != nil {
khenaidoo0458db62019-06-20 08:50:36 -0400425 return status.Errorf(codes.Aborted, "errors-%s", res)
426 }
427 return nil
khenaidoo0458db62019-06-20 08:50:36 -0400428}
429
A R Karthick5c28f552019-12-11 22:47:44 -0800430//updateFlowsAndGroups replaces the existing flows and groups with "updatedFlows" and "updatedGroups" respectively. It
431//also sends the updates to the adapters
Kent Hagerman2b216042020-04-03 18:28:56 -0400432func (agent *Agent) updateFlowsAndGroups(ctx context.Context, updatedFlows []*ofp.OfpFlowStats, updatedGroups []*ofp.OfpGroupEntry, flowMetadata *voltha.FlowMetadata) error {
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700433 var flwResponse, grpResponse coreutils.Response
434 var err error
435 if flwResponse, err = agent.updateFlowsToAdapter(ctx, updatedFlows, flowMetadata); err != nil {
A R Karthick5c28f552019-12-11 22:47:44 -0800436 return err
437 }
Mahir Gunyel03de0d32020-06-03 01:36:59 -0700438 if grpResponse, err = agent.updateGroupsToAdapter(ctx, updatedGroups, flowMetadata); err != nil {
439 return err
440 }
441
442 if res := coreutils.WaitForNilOrErrorResponses(agent.defaultTimeout, flwResponse, grpResponse); res != nil {
khenaidoo0458db62019-06-20 08:50:36 -0400443 return status.Errorf(codes.Aborted, "errors-%s", res)
444 }
445 return nil
khenaidoo19d7b632018-10-30 10:49:50 -0400446}
447
khenaidoo4d4802d2018-10-04 21:59:49 -0400448//disableDevice disable a device
Kent Hagerman2b216042020-04-03 18:28:56 -0400449func (agent *Agent) disableDevice(ctx context.Context) error {
khenaidoo442e7c72020-03-10 16:13:48 -0400450 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
451 return err
452 }
Rohan Agrawal31f21802020-06-12 05:38:46 +0000453 logger.Debugw(ctx, "disableDevice", log.Fields{"device-id": agent.deviceID})
khenaidoo6e55d9e2019-12-12 18:26:26 -0500454
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400455 cloned := agent.cloneDeviceWithoutLock()
khenaidoo6e55d9e2019-12-12 18:26:26 -0500456
457 if cloned.AdminState == voltha.AdminState_DISABLED {
divyadesaicb8b59d2020-08-18 09:55:47 +0000458 logger.Debugw(ctx, "device-already-disabled", log.Fields{"device-id": agent.deviceID})
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400459 agent.requestQueue.RequestComplete()
npujar1d86a522019-11-14 17:11:16 +0530460 return nil
461 }
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530462 if cloned.AdminState == voltha.AdminState_PREPROVISIONED {
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400463 agent.requestQueue.RequestComplete()
khenaidoo6e55d9e2019-12-12 18:26:26 -0500464 return status.Errorf(codes.FailedPrecondition, "deviceId:%s, invalid-admin-state:%s", agent.deviceID, cloned.AdminState)
npujar1d86a522019-11-14 17:11:16 +0530465 }
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530466 if agent.isDeletionInProgress() {
467 agent.requestQueue.RequestComplete()
468 return status.Errorf(codes.FailedPrecondition, "deviceId:%s, Device deletion is in progress.", agent.deviceID)
469 }
npujar1d86a522019-11-14 17:11:16 +0530470 // Update the Admin State and operational state before sending the request out
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400471 cloned.AdminState = voltha.AdminState_DISABLED
472 cloned.OperStatus = voltha.OperStatus_UNKNOWN
473 if err := agent.updateDeviceAndReleaseLock(ctx, cloned); err != nil {
npujar1d86a522019-11-14 17:11:16 +0530474 return err
475 }
khenaidoo442e7c72020-03-10 16:13:48 -0400476
Rohan Agrawalcf12f202020-08-03 04:42:01 +0000477 subCtx, cancel := context.WithTimeout(log.WithSpanFromContext(context.Background(), ctx), agent.defaultTimeout)
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400478 ch, err := agent.adapterProxy.DisableDevice(subCtx, cloned)
khenaidoo442e7c72020-03-10 16:13:48 -0400479 if err != nil {
480 cancel()
npujar1d86a522019-11-14 17:11:16 +0530481 return err
khenaidoo0a822f92019-05-08 15:15:57 -0400482 }
khenaidoo442e7c72020-03-10 16:13:48 -0400483 go agent.waitForAdapterResponse(subCtx, cancel, "disableDevice", ch, agent.onSuccess, agent.onFailure)
khenaidoo0a822f92019-05-08 15:15:57 -0400484
khenaidoo92e62c52018-10-03 14:02:54 -0400485 return nil
486}
487
Kent Hagerman2b216042020-04-03 18:28:56 -0400488func (agent *Agent) rebootDevice(ctx context.Context) error {
khenaidoo442e7c72020-03-10 16:13:48 -0400489 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
npujar1d86a522019-11-14 17:11:16 +0530490 return err
khenaidoo4d4802d2018-10-04 21:59:49 -0400491 }
khenaidoo442e7c72020-03-10 16:13:48 -0400492 defer agent.requestQueue.RequestComplete()
Rohan Agrawal31f21802020-06-12 05:38:46 +0000493 logger.Debugw(ctx, "rebootDevice", log.Fields{"device-id": agent.deviceID})
khenaidoo442e7c72020-03-10 16:13:48 -0400494
Kent Hagermancba2f302020-07-28 13:37:36 -0400495 device := agent.getDeviceReadOnlyWithoutLock()
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530496 if agent.isDeletionInProgress() {
497 return status.Errorf(codes.FailedPrecondition, "deviceId:%s, Device deletion is in progress.", agent.deviceID)
498 }
Rohan Agrawalcf12f202020-08-03 04:42:01 +0000499 subCtx, cancel := context.WithTimeout(log.WithSpanFromContext(context.Background(), ctx), agent.defaultTimeout)
Kent Hagerman2b216042020-04-03 18:28:56 -0400500 ch, err := agent.adapterProxy.RebootDevice(subCtx, device)
khenaidoo442e7c72020-03-10 16:13:48 -0400501 if err != nil {
502 cancel()
503 return err
504 }
505 go agent.waitForAdapterResponse(subCtx, cancel, "rebootDevice", ch, agent.onSuccess, agent.onFailure)
khenaidoo4d4802d2018-10-04 21:59:49 -0400506 return nil
507}
508
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530509func (agent *Agent) deleteDeviceForce(ctx context.Context) error {
510 logger.Debugw(ctx, "deleteDeviceForce", log.Fields{"device-id": agent.deviceID})
511 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
512 return err
513 }
514 // Get the device Transient state, return err if it is DELETING
515 previousDeviceTransientState := agent.getTransientState()
516
517 if agent.isStateDeleting(previousDeviceTransientState) {
518 agent.requestQueue.RequestComplete()
519 return status.Errorf(codes.FailedPrecondition, "deviceId:%s, Device Deletion is in progress",
520 agent.deviceID)
521 }
522 device := agent.cloneDeviceWithoutLock()
523 if err := agent.updateDeviceWithTransientStateAndReleaseLock(ctx, device, voltha.DeviceTransientState_FORCE_DELETING,
524 previousDeviceTransientState); err != nil {
525 return err
526 }
527 previousAdminState := device.AdminState
528 if previousAdminState != ic.AdminState_PREPROVISIONED {
529 subCtx, cancel := context.WithTimeout(log.WithSpanFromContext(context.Background(), ctx), agent.defaultTimeout)
530 ch, err := agent.adapterProxy.DeleteDevice(subCtx, device)
531 if err != nil {
532 cancel()
533 return err
534 }
535 // Since it is a case of force delete, nothing needs to be done on adapter responses.
536 go agent.waitForAdapterResponse(subCtx, cancel, "deleteDeviceForce", ch, agent.onSuccess,
537 agent.onFailure)
538 }
539 return nil
540}
541
Kent Hagerman2b216042020-04-03 18:28:56 -0400542func (agent *Agent) deleteDevice(ctx context.Context) error {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000543 logger.Debugw(ctx, "deleteDevice", log.Fields{"device-id": agent.deviceID})
khenaidoo442e7c72020-03-10 16:13:48 -0400544 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
545 return err
546 }
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530547 // Get the device Transient state, return err if it is DELETING
548 previousDeviceTransientState := agent.getTransientState()
khenaidoo6e55d9e2019-12-12 18:26:26 -0500549
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530550 if agent.isStateDeleting(previousDeviceTransientState) {
551 agent.requestQueue.RequestComplete()
552 return status.Errorf(codes.FailedPrecondition, "deviceId:%s, Device Deletion is in progress", agent.deviceID)
553 }
554 device := agent.cloneDeviceWithoutLock()
555 previousAdminState := device.AdminState
556 // Change the device transient state to DELETING_FROM_ADAPTER state till the device is removed from adapters.
557 currentDeviceTransientState := voltha.DeviceTransientState_DELETING_FROM_ADAPTER
khenaidoo442e7c72020-03-10 16:13:48 -0400558
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530559 if previousAdminState == ic.AdminState_PREPROVISIONED {
560 // Change the state to DELETING POST ADAPTER RESPONSE directly as adapters have no info of the device.
561 currentDeviceTransientState = voltha.DeviceTransientState_DELETING_POST_ADAPTER_RESPONSE
562 }
563 if err := agent.updateDeviceWithTransientStateAndReleaseLock(ctx, device, currentDeviceTransientState,
564 previousDeviceTransientState); err != nil {
npujar1d86a522019-11-14 17:11:16 +0530565 return err
566 }
khenaidoo442e7c72020-03-10 16:13:48 -0400567 // If the device was in pre-prov state (only parent device are in that state) then do not send the request to the
568 // adapter
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530569 if previousAdminState != ic.AdminState_PREPROVISIONED {
Rohan Agrawalcf12f202020-08-03 04:42:01 +0000570 subCtx, cancel := context.WithTimeout(log.WithSpanFromContext(context.Background(), ctx), agent.defaultTimeout)
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530571 ch, err := agent.adapterProxy.DeleteDevice(subCtx, device)
khenaidoo442e7c72020-03-10 16:13:48 -0400572 if err != nil {
573 cancel()
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530574 //updating of transient state is required in error
575 if err := agent.updateTransientState(ctx, voltha.DeviceTransientState_DELETE_FAILED); err != nil {
576 logger.Errorw(ctx, "failed-to-update-transient-state-as-delete-failed", log.Fields{"device-id": agent.deviceID})
577 }
khenaidoo442e7c72020-03-10 16:13:48 -0400578 return err
579 }
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530580 go agent.waitForAdapterDeleteResponse(subCtx, cancel, "deleteDevice", ch, agent.onDeleteSuccess,
581 agent.onDeleteFailure)
khenaidoo442e7c72020-03-10 16:13:48 -0400582 }
khenaidoo4d4802d2018-10-04 21:59:49 -0400583 return nil
584}
585
Kent Hagerman2b216042020-04-03 18:28:56 -0400586func (agent *Agent) setParentID(ctx context.Context, device *voltha.Device, parentID string) error {
khenaidoo442e7c72020-03-10 16:13:48 -0400587 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
588 return err
589 }
Rohan Agrawal31f21802020-06-12 05:38:46 +0000590 logger.Debugw(ctx, "setParentId", log.Fields{"device-id": device.Id, "parent-id": parentID})
khenaidoo6e55d9e2019-12-12 18:26:26 -0500591
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400592 cloned := agent.cloneDeviceWithoutLock()
npujar1d86a522019-11-14 17:11:16 +0530593 cloned.ParentId = parentID
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400594 return agent.updateDeviceAndReleaseLock(ctx, cloned)
khenaidooad06fd72019-10-28 12:26:05 -0400595}
596
khenaidoo442e7c72020-03-10 16:13:48 -0400597// getSwitchCapability retrieves the switch capability of a parent device
Kent Hagerman2b216042020-04-03 18:28:56 -0400598func (agent *Agent) getSwitchCapability(ctx context.Context) (*ic.SwitchCapability, error) {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000599 logger.Debugw(ctx, "getSwitchCapability", log.Fields{"device-id": agent.deviceID})
khenaidoo442e7c72020-03-10 16:13:48 -0400600
Kent Hagermancba2f302020-07-28 13:37:36 -0400601 device, err := agent.getDeviceReadOnly(ctx)
khenaidoo442e7c72020-03-10 16:13:48 -0400602 if err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400603 return nil, err
khenaidoob9203542018-09-17 22:56:37 -0400604 }
Kent Hagermancba2f302020-07-28 13:37:36 -0400605 ch, err := agent.adapterProxy.GetOfpDeviceInfo(ctx, device)
khenaidoo442e7c72020-03-10 16:13:48 -0400606 if err != nil {
607 return nil, err
608 }
609
610 // Wait for adapter response
611 rpcResponse, ok := <-ch
612 if !ok {
613 return nil, status.Errorf(codes.Aborted, "channel-closed")
614 }
615 if rpcResponse.Err != nil {
616 return nil, rpcResponse.Err
617 }
618 // Successful response
619 switchCap := &ic.SwitchCapability{}
620 if err := ptypes.UnmarshalAny(rpcResponse.Reply, switchCap); err != nil {
npujar1d86a522019-11-14 17:11:16 +0530621 return nil, err
622 }
623 return switchCap, nil
khenaidoob9203542018-09-17 22:56:37 -0400624}
625
Rohan Agrawal31f21802020-06-12 05:38:46 +0000626func (agent *Agent) onPacketFailure(ctx context.Context, rpc string, response interface{}, args ...interface{}) {
khenaidoo442e7c72020-03-10 16:13:48 -0400627 // packet data is encoded in the args param as the first parameter
628 var packet []byte
629 if len(args) >= 1 {
630 if pkt, ok := args[0].([]byte); ok {
631 packet = pkt
632 }
633 }
634 var errResp error
635 if err, ok := response.(error); ok {
636 errResp = err
637 }
Rohan Agrawal31f21802020-06-12 05:38:46 +0000638 logger.Warnw(ctx, "packet-out-error", log.Fields{
khenaidoo442e7c72020-03-10 16:13:48 -0400639 "device-id": agent.deviceID,
640 "error": errResp,
641 "packet": hex.EncodeToString(packet),
642 })
643}
644
Kent Hagerman2b216042020-04-03 18:28:56 -0400645func (agent *Agent) packetOut(ctx context.Context, outPort uint32, packet *ofp.OfpPacketOut) error {
Scott Baker80678602019-11-14 16:57:36 -0800646 // If deviceType=="" then we must have taken ownership of this device.
647 // Fixes VOL-2226 where a core would take ownership and have stale data
648 if agent.deviceType == "" {
npujar467fe752020-01-16 20:17:45 +0530649 agent.reconcileWithKVStore(ctx)
Scott Baker80678602019-11-14 16:57:36 -0800650 }
khenaidoofdbad6e2018-11-06 22:26:38 -0500651 // Send packet to adapter
Rohan Agrawalcf12f202020-08-03 04:42:01 +0000652 subCtx, cancel := context.WithTimeout(log.WithSpanFromContext(context.Background(), ctx), agent.defaultTimeout)
Kent Hagerman2b216042020-04-03 18:28:56 -0400653 ch, err := agent.adapterProxy.PacketOut(subCtx, agent.deviceType, agent.deviceID, outPort, packet)
khenaidoo442e7c72020-03-10 16:13:48 -0400654 if err != nil {
655 cancel()
656 return nil
khenaidoofdbad6e2018-11-06 22:26:38 -0500657 }
khenaidoo442e7c72020-03-10 16:13:48 -0400658 go agent.waitForAdapterResponse(subCtx, cancel, "packetOut", ch, agent.onSuccess, agent.onPacketFailure, packet.Data)
khenaidoofdbad6e2018-11-06 22:26:38 -0500659 return nil
660}
661
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400662func (agent *Agent) updateDeviceUsingAdapterData(ctx context.Context, device *voltha.Device) error {
663 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
664 return err
665 }
666 logger.Debugw(ctx, "updateDeviceUsingAdapterData", log.Fields{"device-id": device.Id})
667
668 cloned := agent.cloneDeviceWithoutLock()
Mahir Gunyel8e2707d2019-07-25 00:36:21 -0700669 cloned.Root = device.Root
670 cloned.Vendor = device.Vendor
671 cloned.Model = device.Model
672 cloned.SerialNumber = device.SerialNumber
673 cloned.MacAddress = device.MacAddress
674 cloned.Vlan = device.Vlan
675 cloned.Reason = device.Reason
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400676 return agent.updateDeviceAndReleaseLock(ctx, cloned)
khenaidoo43c82122018-11-22 18:38:28 -0500677}
678
Kent Hagerman2b216042020-04-03 18:28:56 -0400679func (agent *Agent) updateDeviceStatus(ctx context.Context, operStatus voltha.OperStatus_Types, connStatus voltha.ConnectStatus_Types) error {
khenaidoo442e7c72020-03-10 16:13:48 -0400680 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
681 return err
682 }
khenaidoo6e55d9e2019-12-12 18:26:26 -0500683
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400684 cloned := agent.cloneDeviceWithoutLock()
npujar1d86a522019-11-14 17:11:16 +0530685 // 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 -0400686 if s, ok := voltha.ConnectStatus_Types_name[int32(connStatus)]; ok {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000687 logger.Debugw(ctx, "updateDeviceStatus-conn", log.Fields{"ok": ok, "val": s})
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400688 cloned.ConnectStatus = connStatus
npujar1d86a522019-11-14 17:11:16 +0530689 }
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400690 if s, ok := voltha.OperStatus_Types_name[int32(operStatus)]; ok {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000691 logger.Debugw(ctx, "updateDeviceStatus-oper", log.Fields{"ok": ok, "val": s})
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400692 cloned.OperStatus = operStatus
npujar1d86a522019-11-14 17:11:16 +0530693 }
divyadesaicb8b59d2020-08-18 09:55:47 +0000694 logger.Debugw(ctx, "updateDeviceStatus", log.Fields{"device-id": cloned.Id, "operStatus": cloned.OperStatus, "connectStatus": cloned.ConnectStatus})
npujar1d86a522019-11-14 17:11:16 +0530695 // Store the device
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400696 return agent.updateDeviceAndReleaseLock(ctx, cloned)
khenaidoo92e62c52018-10-03 14:02:54 -0400697}
698
khenaidoob9203542018-09-17 22:56:37 -0400699// TODO: A generic device update by attribute
Kent Hagerman2b216042020-04-03 18:28:56 -0400700func (agent *Agent) updateDeviceAttribute(ctx context.Context, name string, value interface{}) {
khenaidoob9203542018-09-17 22:56:37 -0400701 if value == nil {
702 return
703 }
khenaidoo6e55d9e2019-12-12 18:26:26 -0500704
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400705 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
706 logger.Warnw(ctx, "request-aborted", log.Fields{"device-id": agent.deviceID, "name": name, "error": err})
707 return
708 }
709
710 cloned := agent.cloneDeviceWithoutLock()
khenaidoob9203542018-09-17 22:56:37 -0400711 updated := false
khenaidoo6e55d9e2019-12-12 18:26:26 -0500712 s := reflect.ValueOf(cloned).Elem()
khenaidoob9203542018-09-17 22:56:37 -0400713 if s.Kind() == reflect.Struct {
714 // exported field
715 f := s.FieldByName(name)
716 if f.IsValid() && f.CanSet() {
717 switch f.Kind() {
718 case reflect.String:
719 f.SetString(value.(string))
720 updated = true
721 case reflect.Uint32:
722 f.SetUint(uint64(value.(uint32)))
723 updated = true
724 case reflect.Bool:
725 f.SetBool(value.(bool))
726 updated = true
727 }
728 }
729 }
divyadesaicb8b59d2020-08-18 09:55:47 +0000730 logger.Debugw(ctx, "update-field-status", log.Fields{"device-id": cloned.Id, "name": name, "updated": updated})
khenaidoob9203542018-09-17 22:56:37 -0400731 // Save the data
khenaidoo6e55d9e2019-12-12 18:26:26 -0500732
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400733 if err := agent.updateDeviceAndReleaseLock(ctx, cloned); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000734 logger.Warnw(ctx, "attribute-update-failed", log.Fields{"attribute": name, "value": value})
khenaidoob9203542018-09-17 22:56:37 -0400735 }
khenaidoob9203542018-09-17 22:56:37 -0400736}
serkant.uluderya334479d2019-04-10 08:26:15 -0700737
Kent Hagerman45a13e42020-04-13 12:23:50 -0400738func (agent *Agent) simulateAlarm(ctx context.Context, simulateReq *voltha.SimulateAlarmRequest) error {
khenaidoo442e7c72020-03-10 16:13:48 -0400739 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
740 return err
741 }
742 defer agent.requestQueue.RequestComplete()
divyadesaicb8b59d2020-08-18 09:55:47 +0000743 logger.Debugw(ctx, "simulateAlarm", log.Fields{"device-id": agent.deviceID})
khenaidoo6e55d9e2019-12-12 18:26:26 -0500744
Kent Hagermancba2f302020-07-28 13:37:36 -0400745 device := agent.getDeviceReadOnlyWithoutLock()
khenaidoo6e55d9e2019-12-12 18:26:26 -0500746
Rohan Agrawalcf12f202020-08-03 04:42:01 +0000747 subCtx, cancel := context.WithTimeout(log.WithSpanFromContext(context.Background(), ctx), agent.defaultTimeout)
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400748 ch, err := agent.adapterProxy.SimulateAlarm(subCtx, device, simulateReq)
khenaidoo442e7c72020-03-10 16:13:48 -0400749 if err != nil {
750 cancel()
npujar1d86a522019-11-14 17:11:16 +0530751 return err
serkant.uluderya334479d2019-04-10 08:26:15 -0700752 }
khenaidoo442e7c72020-03-10 16:13:48 -0400753 go agent.waitForAdapterResponse(subCtx, cancel, "simulateAlarm", ch, agent.onSuccess, agent.onFailure)
serkant.uluderya334479d2019-04-10 08:26:15 -0700754 return nil
755}
Mahir Gunyelb5851672019-07-24 10:46:26 +0300756
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400757// This function updates the device in the DB, releases the device lock, and runs any state transitions.
758// The calling function MUST hold the device lock. The caller MUST NOT modify the device after this is called.
759func (agent *Agent) updateDeviceAndReleaseLock(ctx context.Context, device *voltha.Device) error {
760 // fail early if this agent is no longer valid
Kent Hagerman4f355f52020-03-30 16:01:33 -0400761 if agent.stopped {
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400762 agent.requestQueue.RequestComplete()
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530763 return errors.New("device-agent-stopped")
Thomas Lee Se5a44012019-11-07 20:32:24 +0530764 }
Kent Hagerman4f355f52020-03-30 16:01:33 -0400765
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400766 // update in db
Kent Hagermanf5a67352020-04-30 15:15:26 -0400767 if err := agent.dbProxy.Set(ctx, agent.deviceID, device); err != nil {
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400768 agent.requestQueue.RequestComplete()
Kent Hagerman4f355f52020-03-30 16:01:33 -0400769 return status.Errorf(codes.Internal, "failed-update-device:%s: %s", agent.deviceID, err)
Mahir Gunyelb5851672019-07-24 10:46:26 +0300770 }
divyadesaicb8b59d2020-08-18 09:55:47 +0000771 logger.Debugw(ctx, "updated-device-in-store", log.Fields{"device-id: ": agent.deviceID})
Mahir Gunyelb5851672019-07-24 10:46:26 +0300772
Kent Hagerman6031aad2020-07-29 16:36:33 -0400773 prevDevice := agent.device
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400774 // update the device
khenaidoo0db4c812020-05-27 15:27:30 -0400775 agent.device = device
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400776
777 // release lock before processing transition
778 agent.requestQueue.RequestComplete()
779
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530780 if err := agent.deviceMgr.stateTransitions.ProcessTransition(log.WithSpanFromContext(context.Background(), ctx),
781 device, prevDevice, voltha.DeviceTransientState_NONE, voltha.DeviceTransientState_NONE); err != nil {
Kent Hagerman6031aad2020-07-29 16:36:33 -0400782 logger.Errorw(ctx, "failed-process-transition", log.Fields{"device-id": device.Id, "previousAdminState": prevDevice.AdminState, "currentAdminState": device.AdminState})
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400783 }
Mahir Gunyelb5851672019-07-24 10:46:26 +0300784 return nil
785}
Mahir Gunyelfdee9212019-10-16 16:52:21 -0700786
Himani Chawla2ba1c9c2020-10-07 13:19:03 +0530787// This function updates the device transient in the DB through loader, releases the device lock, and runs any state transitions.
788// The calling function MUST hold the device lock. The caller MUST NOT modify the device after this is called.
789func (agent *Agent) updateDeviceWithTransientStateAndReleaseLock(ctx context.Context, device *voltha.Device,
790 transientState, prevTransientState voltha.DeviceTransientState_Types) error {
791 // fail early if this agent is no longer valid
792 if agent.stopped {
793 agent.requestQueue.RequestComplete()
794 return errors.New("device-agent-stopped")
795 }
796 //update device TransientState
797 if err := agent.updateTransientState(ctx, transientState); err != nil {
798 agent.requestQueue.RequestComplete()
799 return err
800 }
801 // update in db
802 if err := agent.dbProxy.Set(ctx, agent.deviceID, device); err != nil {
803 //Reverting TransientState update
804 err := agent.updateTransientState(ctx, prevTransientState)
805 logger.Errorw(ctx, "failed-to-revert-transient-state-update-on-error", log.Fields{"device-id": device.Id,
806 "previousTransientState": prevTransientState, "currentTransientState": transientState})
807 agent.requestQueue.RequestComplete()
808 return status.Errorf(codes.Internal, "failed-update-device:%s: %s", agent.deviceID, err)
809 }
810
811 logger.Debugw(ctx, "updated-device-in-store", log.Fields{"device-id: ": agent.deviceID})
812
813 prevDevice := agent.device
814 // update the device
815 agent.device = device
816
817 // release lock before processing transition
818 agent.requestQueue.RequestComplete()
819
820 if err := agent.deviceMgr.stateTransitions.ProcessTransition(log.WithSpanFromContext(context.Background(), ctx),
821 device, prevDevice, transientState, prevTransientState); err != nil {
822 logger.Errorw(ctx, "failed-process-transition", log.Fields{"device-id": device.Id, "previousAdminState": prevDevice.AdminState, "currentAdminState": device.AdminState})
823 }
824 return nil
825}
Kent Hagerman2b216042020-04-03 18:28:56 -0400826func (agent *Agent) updateDeviceReason(ctx context.Context, reason string) error {
khenaidoo442e7c72020-03-10 16:13:48 -0400827 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
828 return err
829 }
divyadesaicb8b59d2020-08-18 09:55:47 +0000830 logger.Debugw(ctx, "updateDeviceReason", log.Fields{"device-id": agent.deviceID, "reason": reason})
khenaidoo6e55d9e2019-12-12 18:26:26 -0500831
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400832 cloned := agent.cloneDeviceWithoutLock()
npujar1d86a522019-11-14 17:11:16 +0530833 cloned.Reason = reason
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400834 return agent.updateDeviceAndReleaseLock(ctx, cloned)
Mahir Gunyelfdee9212019-10-16 16:52:21 -0700835}
kesavandbc2d1622020-01-21 00:42:01 -0500836
Kent Hagerman2b216042020-04-03 18:28:56 -0400837func (agent *Agent) ChildDeviceLost(ctx context.Context, device *voltha.Device) error {
divyadesaicb8b59d2020-08-18 09:55:47 +0000838 logger.Debugw(ctx, "childDeviceLost", log.Fields{"child-device-id": device.Id, "parent-device-id": agent.deviceID})
Chaitrashree G S543df3e2020-02-24 22:36:54 -0500839
Kent Hagerman2a07b862020-06-19 15:23:07 -0400840 // Remove the associated peer ports on the parent device
841 for portID := range agent.portLoader.ListIDs() {
842 if portHandle, have := agent.portLoader.Lock(portID); have {
843 oldPort := portHandle.GetReadOnly()
844 updatedPeers := make([]*voltha.Port_PeerPort, 0)
845 for _, peerPort := range oldPort.Peers {
846 if peerPort.DeviceId != device.Id {
847 updatedPeers = append(updatedPeers, peerPort)
848 }
khenaidoo442e7c72020-03-10 16:13:48 -0400849 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400850 newPort := *oldPort
851 newPort.Peers = updatedPeers
852 if err := portHandle.Update(ctx, &newPort); err != nil {
853 portHandle.Unlock()
854 return nil
855 }
856 portHandle.Unlock()
khenaidoo442e7c72020-03-10 16:13:48 -0400857 }
Chaitrashree G S543df3e2020-02-24 22:36:54 -0500858 }
859
khenaidoo442e7c72020-03-10 16:13:48 -0400860 //send request to adapter
Rohan Agrawalcf12f202020-08-03 04:42:01 +0000861 subCtx, cancel := context.WithTimeout(log.WithSpanFromContext(context.Background(), ctx), agent.defaultTimeout)
Kent Hagerman2b216042020-04-03 18:28:56 -0400862 ch, err := agent.adapterProxy.ChildDeviceLost(ctx, agent.deviceType, agent.deviceID, device.ParentPortNo, device.ProxyAddress.OnuId)
khenaidoo442e7c72020-03-10 16:13:48 -0400863 if err != nil {
864 cancel()
865 return err
Chaitrashree G S543df3e2020-02-24 22:36:54 -0500866 }
khenaidoo442e7c72020-03-10 16:13:48 -0400867 go agent.waitForAdapterResponse(subCtx, cancel, "childDeviceLost", ch, agent.onSuccess, agent.onFailure)
Chaitrashree G S543df3e2020-02-24 22:36:54 -0500868 return nil
Chaitrashree G S543df3e2020-02-24 22:36:54 -0500869}
onkarkundargi87285252020-01-27 11:34:52 +0530870
Kent Hagerman2b216042020-04-03 18:28:56 -0400871func (agent *Agent) startOmciTest(ctx context.Context, omcitestrequest *voltha.OmciTestRequest) (*voltha.TestResponse, error) {
onkarkundargi87285252020-01-27 11:34:52 +0530872 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
873 return nil, err
874 }
875
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400876 cloned := agent.cloneDeviceWithoutLock()
Matteo Scandolod525ae32020-04-02 17:27:29 -0700877
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400878 if cloned.Adapter == "" {
879 adapterName, err := agent.adapterMgr.GetAdapterType(cloned.Type)
Matteo Scandolod525ae32020-04-02 17:27:29 -0700880 if err != nil {
881 agent.requestQueue.RequestComplete()
882 return nil, err
883 }
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400884 cloned.Adapter = adapterName
onkarkundargi87285252020-01-27 11:34:52 +0530885 }
886
887 // Send request to the adapter
Kent Hagermanf6db9f12020-07-22 17:16:19 -0400888 ch, err := agent.adapterProxy.StartOmciTest(ctx, cloned, omcitestrequest)
onkarkundargi87285252020-01-27 11:34:52 +0530889 agent.requestQueue.RequestComplete()
890 if err != nil {
891 return nil, err
892 }
893
894 // Wait for the adapter response
895 rpcResponse, ok := <-ch
896 if !ok {
897 return nil, status.Errorf(codes.Aborted, "channel-closed-device-id-%s", agent.deviceID)
898 }
899 if rpcResponse.Err != nil {
900 return nil, rpcResponse.Err
901 }
902
903 // Unmarshal and return the response
904 testResp := &voltha.TestResponse{}
905 if err := ptypes.UnmarshalAny(rpcResponse.Reply, testResp); err != nil {
906 return nil, status.Errorf(codes.InvalidArgument, "%s", err.Error())
907 }
Rohan Agrawal31f21802020-06-12 05:38:46 +0000908 logger.Debugw(ctx, "Omci_test_Request-Success-device-agent", log.Fields{"testResp": testResp})
onkarkundargi87285252020-01-27 11:34:52 +0530909 return testResp, nil
910}
Dinesh Belwalkarc1129f12020-02-27 10:41:33 -0800911
912func (agent *Agent) getExtValue(ctx context.Context, pdevice *voltha.Device, cdevice *voltha.Device, valueparam *voltha.ValueSpecifier) (*voltha.ReturnValues, error) {
Girish Kumar3e8ee212020-08-19 17:50:11 +0000913 logger.Debugw(ctx, "getExtValue", log.Fields{"device-id": agent.deviceID, "onuid": valueparam.Id, "valuetype": valueparam.Value})
Dinesh Belwalkarc1129f12020-02-27 10:41:33 -0800914 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
915 return nil, err
916 }
917
918 //send request to adapter
919 ch, err := agent.adapterProxy.GetExtValue(ctx, pdevice, cdevice, valueparam.Id, valueparam.Value)
920 agent.requestQueue.RequestComplete()
921 if err != nil {
922 return nil, err
923 }
924
925 // Wait for the adapter response
926 rpcResponse, ok := <-ch
927 if !ok {
928 return nil, status.Errorf(codes.Aborted, "channel-closed-device-id-%s", agent.deviceID)
929 }
930 if rpcResponse.Err != nil {
931 return nil, rpcResponse.Err
932 }
933
934 // Unmarshal and return the response
935 Resp := &voltha.ReturnValues{}
936 if err := ptypes.UnmarshalAny(rpcResponse.Reply, Resp); err != nil {
937 return nil, status.Errorf(codes.InvalidArgument, "%s", err.Error())
938 }
Rohan Agrawal31f21802020-06-12 05:38:46 +0000939 logger.Debugw(ctx, "getExtValue-Success-device-agent", log.Fields{"Resp": Resp})
Dinesh Belwalkarc1129f12020-02-27 10:41:33 -0800940 return Resp, nil
941}
dpaul62686312020-06-23 14:17:36 +0530942
943func (agent *Agent) setExtValue(ctx context.Context, device *voltha.Device, value *voltha.ValueSet) (*empty.Empty, error) {
944 logger.Debugw(ctx, "setExtValue", log.Fields{"device-id": value.Id})
945 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
946 return nil, err
947 }
948
949 //send request to adapter
950 ch, err := agent.adapterProxy.SetExtValue(ctx, device, value)
951 agent.requestQueue.RequestComplete()
952 if err != nil {
953 return nil, err
954 }
955
956 // Wait for the adapter response
957 rpcResponse, ok := <-ch
958 if !ok {
959 return nil, status.Errorf(codes.Aborted, "channel-closed-device-id-%s", agent.deviceID)
960 }
961 if rpcResponse.Err != nil {
962 return nil, rpcResponse.Err
963 }
964
965 // Unmarshal and return the response
966 logger.Debug(ctx, "setExtValue-Success-device-agent")
967 return &empty.Empty{}, nil
968}