blob: 52ab584a6a608d97414f784d9f53afad326e3990 [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 */
16package core
17
18import (
19 "context"
khenaidoo92e62c52018-10-03 14:02:54 -040020 "reflect"
21 "sync"
22
khenaidoob9203542018-09-17 22:56:37 -040023 "github.com/gogo/protobuf/proto"
24 "github.com/opencord/voltha-go/common/log"
25 "github.com/opencord/voltha-go/db/model"
26 "github.com/opencord/voltha-go/protos/core_adapter"
27 "github.com/opencord/voltha-go/protos/voltha"
28 "google.golang.org/grpc/codes"
29 "google.golang.org/grpc/status"
khenaidoob9203542018-09-17 22:56:37 -040030)
31
32type DeviceAgent struct {
khenaidoo9a468962018-09-19 15:33:13 -040033 deviceId string
34 lastData *voltha.Device
35 adapterProxy *AdapterProxy
36 deviceMgr *DeviceManager
37 clusterDataProxy *model.Proxy
khenaidoo92e62c52018-10-03 14:02:54 -040038 deviceProxy *model.Proxy
khenaidoo9a468962018-09-19 15:33:13 -040039 exitChannel chan int
khenaidoo92e62c52018-10-03 14:02:54 -040040 lockDevice sync.RWMutex
khenaidoob9203542018-09-17 22:56:37 -040041}
42
khenaidoo4d4802d2018-10-04 21:59:49 -040043//newDeviceAgent creates a new device agent along as creating a unique ID for the device and set the device state to
44//preprovisioning
khenaidoo9a468962018-09-19 15:33:13 -040045func newDeviceAgent(ap *AdapterProxy, device *voltha.Device, deviceMgr *DeviceManager, cdProxy *model.Proxy) *DeviceAgent {
khenaidoob9203542018-09-17 22:56:37 -040046 var agent DeviceAgent
khenaidoob9203542018-09-17 22:56:37 -040047 agent.adapterProxy = ap
khenaidoo92e62c52018-10-03 14:02:54 -040048 cloned := (proto.Clone(device)).(*voltha.Device)
49 cloned.Id = CreateDeviceId()
50 cloned.AdminState = voltha.AdminState_PREPROVISIONED
51 agent.deviceId = cloned.Id
52 agent.lastData = cloned
khenaidoob9203542018-09-17 22:56:37 -040053 agent.deviceMgr = deviceMgr
54 agent.exitChannel = make(chan int, 1)
khenaidoo9a468962018-09-19 15:33:13 -040055 agent.clusterDataProxy = cdProxy
khenaidoo92e62c52018-10-03 14:02:54 -040056 agent.lockDevice = sync.RWMutex{}
khenaidoob9203542018-09-17 22:56:37 -040057 return &agent
58}
59
khenaidoo4d4802d2018-10-04 21:59:49 -040060// start save the device to the data model and registers for callbacks on that device
khenaidoob9203542018-09-17 22:56:37 -040061func (agent *DeviceAgent) start(ctx context.Context) {
khenaidoo92e62c52018-10-03 14:02:54 -040062 agent.lockDevice.Lock()
63 defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -040064 log.Debugw("starting-device-agent", log.Fields{"device": agent.lastData})
65 // Add the initial device to the local model
khenaidoo9a468962018-09-19 15:33:13 -040066 if added := agent.clusterDataProxy.Add("/devices", agent.lastData, ""); added == nil {
khenaidoob9203542018-09-17 22:56:37 -040067 log.Errorw("failed-to-add-device", log.Fields{"deviceId": agent.deviceId})
68 }
khenaidoo92e62c52018-10-03 14:02:54 -040069 agent.deviceProxy = agent.clusterDataProxy.Root.GetProxy("/devices/"+agent.deviceId, false)
khenaidoo92e62c52018-10-03 14:02:54 -040070 agent.deviceProxy.RegisterCallback(model.POST_UPDATE, agent.processUpdate, nil)
khenaidoob9203542018-09-17 22:56:37 -040071 log.Debug("device-agent-started")
72}
73
khenaidoo4d4802d2018-10-04 21:59:49 -040074// stop stops the device agent. Not much to do for now
75func (agent *DeviceAgent) stop(ctx context.Context) {
khenaidoo92e62c52018-10-03 14:02:54 -040076 agent.lockDevice.Lock()
77 defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -040078 log.Debug("stopping-device-agent")
79 agent.exitChannel <- 1
80 log.Debug("device-agent-stopped")
81}
82
khenaidoo4d4802d2018-10-04 21:59:49 -040083// getDevice retrieves the latest device information from the data model
khenaidoo92e62c52018-10-03 14:02:54 -040084func (agent *DeviceAgent) getDevice() (*voltha.Device, error) {
85 agent.lockDevice.Lock()
86 defer agent.lockDevice.Unlock()
87 if device := agent.clusterDataProxy.Get("/devices/"+agent.deviceId, 1, false, ""); device != nil {
88 if d, ok := device.(*voltha.Device); ok {
89 cloned := proto.Clone(d).(*voltha.Device)
90 return cloned, nil
91 }
92 }
93 return nil, status.Errorf(codes.NotFound, "device-%s", agent.deviceId)
94}
95
khenaidoo4d4802d2018-10-04 21:59:49 -040096// getDeviceWithoutLock is a helper function to be used ONLY by any device agent function AFTER it has acquired the device lock.
khenaidoo92e62c52018-10-03 14:02:54 -040097// This function is meant so that we do not have duplicate code all over the device agent functions
98func (agent *DeviceAgent) getDeviceWithoutLock() (*voltha.Device, error) {
99 if device := agent.clusterDataProxy.Get("/devices/"+agent.deviceId, 1, false, ""); device != nil {
100 if d, ok := device.(*voltha.Device); ok {
101 cloned := proto.Clone(d).(*voltha.Device)
102 return cloned, nil
103 }
104 }
105 return nil, status.Errorf(codes.NotFound, "device-%s", agent.deviceId)
106}
107
khenaidoo4d4802d2018-10-04 21:59:49 -0400108// enableDevice activates a preprovisioned or disable device
khenaidoob9203542018-09-17 22:56:37 -0400109func (agent *DeviceAgent) enableDevice(ctx context.Context) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400110 agent.lockDevice.Lock()
111 defer agent.lockDevice.Unlock()
112 log.Debugw("enableDevice", log.Fields{"id": agent.deviceId})
113 if device, err := agent.getDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400114 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
115 } else {
khenaidoo92e62c52018-10-03 14:02:54 -0400116 if device.AdminState == voltha.AdminState_ENABLED {
117 log.Debugw("device-already-enabled", log.Fields{"id": agent.deviceId})
118 //TODO: Needs customized error message
119 return nil
120 }
khenaidoo4d4802d2018-10-04 21:59:49 -0400121 //TODO: if parent device is disabled then do not enable device
khenaidoo92e62c52018-10-03 14:02:54 -0400122 // Verify whether we need to adopt the device the first time
123 // TODO: A state machine for these state transitions would be better (we just have to handle
124 // a limited set of states now or it may be an overkill)
125 if device.AdminState == voltha.AdminState_PREPROVISIONED {
126 // First send the request to an Adapter and wait for a response
127 if err := agent.adapterProxy.AdoptDevice(ctx, device); err != nil {
128 log.Debugw("adoptDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
khenaidoob9203542018-09-17 22:56:37 -0400129 return err
130 }
khenaidoo92e62c52018-10-03 14:02:54 -0400131 } else {
132 // First send the request to an Adapter and wait for a response
133 if err := agent.adapterProxy.ReEnableDevice(ctx, device); err != nil {
134 log.Debugw("renableDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
135 return err
136 }
137 }
138 // Received an Ack (no error found above). Now update the device in the model to the expected state
139 cloned := proto.Clone(device).(*voltha.Device)
140 cloned.AdminState = voltha.AdminState_ENABLED
141 cloned.OperStatus = voltha.OperStatus_ACTIVATING
142 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
143 return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
khenaidoob9203542018-09-17 22:56:37 -0400144 }
145 }
146 return nil
147}
148
khenaidoo4d4802d2018-10-04 21:59:49 -0400149//disableDevice disable a device
khenaidoo92e62c52018-10-03 14:02:54 -0400150func (agent *DeviceAgent) disableDevice(ctx context.Context) error {
151 agent.lockDevice.Lock()
152 //defer agent.lockDevice.Unlock()
153 log.Debugw("disableDevice", log.Fields{"id": agent.deviceId})
154 // Get the most up to date the device info
155 if device, err := agent.getDeviceWithoutLock(); err != nil {
156 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
157 } else {
158 if device.AdminState == voltha.AdminState_DISABLED {
159 log.Debugw("device-already-disabled", log.Fields{"id": agent.deviceId})
160 //TODO: Needs customized error message
161 agent.lockDevice.Unlock()
162 return nil
163 }
164 // First send the request to an Adapter and wait for a response
165 if err := agent.adapterProxy.DisableDevice(ctx, device); err != nil {
166 log.Debugw("disableDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
167 agent.lockDevice.Unlock()
168 return err
169 }
170 // Received an Ack (no error found above). Now update the device in the model to the expected state
171 cloned := proto.Clone(device).(*voltha.Device)
172 cloned.AdminState = voltha.AdminState_DISABLED
173 // Set the state of all ports on that device to disable
174 for _, port := range cloned.Ports {
175 port.AdminState = voltha.AdminState_DISABLED
176 port.OperStatus = voltha.OperStatus_UNKNOWN
177 }
178 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
179 agent.lockDevice.Unlock()
180 return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
181 }
182 agent.lockDevice.Unlock()
183 //TODO: callback will be invoked to handle this state change
184 //For now force the state transition to happen
185 if err := agent.deviceMgr.processTransition(device, cloned); err != nil {
186 log.Warnw("process-transition-error", log.Fields{"deviceid": device.Id, "error": err})
187 return err
188 }
189 }
190 return nil
191}
192
khenaidoo4d4802d2018-10-04 21:59:49 -0400193func (agent *DeviceAgent) rebootDevice(ctx context.Context) error {
194 agent.lockDevice.Lock()
195 defer agent.lockDevice.Unlock()
196 log.Debugw("rebootDevice", log.Fields{"id": agent.deviceId})
197 // Get the most up to date the device info
198 if device, err := agent.getDeviceWithoutLock(); err != nil {
199 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
200 } else {
201 if device.AdminState != voltha.AdminState_DISABLED {
202 log.Debugw("device-not-disabled", log.Fields{"id": agent.deviceId})
203 //TODO: Needs customized error message
204 return status.Errorf(codes.FailedPrecondition, "deviceId:%s, expected-admin-state:%s", agent.deviceId, voltha.AdminState_DISABLED)
205 }
206 // First send the request to an Adapter and wait for a response
207 if err := agent.adapterProxy.RebootDevice(ctx, device); err != nil {
208 log.Debugw("rebootDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
209 return err
210 }
211 }
212 return nil
213}
214
215func (agent *DeviceAgent) deleteDevice(ctx context.Context) error {
216 agent.lockDevice.Lock()
217 log.Debugw("deleteDevice", log.Fields{"id": agent.deviceId})
218 // Get the most up to date the device info
219 if device, err := agent.getDeviceWithoutLock(); err != nil {
220 agent.lockDevice.Unlock()
221 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
222 } else {
223 if device.AdminState != voltha.AdminState_DISABLED {
224 log.Debugw("device-not-disabled", log.Fields{"id": agent.deviceId})
225 //TODO: Needs customized error message
226 agent.lockDevice.Unlock()
227 return status.Errorf(codes.FailedPrecondition, "deviceId:%s, expected-admin-state:%s", agent.deviceId, voltha.AdminState_DISABLED)
228 }
229 // Send the request to an Adapter and wait for a response
230 if err := agent.adapterProxy.DeleteDevice(ctx, device); err != nil {
231 log.Debugw("deleteDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
232 agent.lockDevice.Unlock()
233 return err
234 }
235 // Set the device Admin state to DELETED in order to trigger the callback to delete
236 // child devices, if any
237 // Received an Ack (no error found above). Now update the device in the model to the expected state
238 cloned := proto.Clone(device).(*voltha.Device)
239 cloned.AdminState = voltha.AdminState_DELETED
240 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
241 agent.lockDevice.Unlock()
242 return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
243 }
244 agent.lockDevice.Unlock()
245 //TODO: callback will be invoked to handle this state change
246 //For now force the state transition to happen
247 if err := agent.deviceMgr.processTransition(device, cloned); err != nil {
248 log.Warnw("process-transition-error", log.Fields{"deviceid": device.Id, "error": err})
249 return err
250 }
251
252 }
253 return nil
254}
255
256// getPorts retrieves the ports information of the device based on the port type.
khenaidoo92e62c52018-10-03 14:02:54 -0400257func (agent *DeviceAgent) getPorts(ctx context.Context, portType voltha.Port_PortType) *voltha.Ports {
258 log.Debugw("getPorts", log.Fields{"id": agent.deviceId, "portType": portType})
khenaidoob9203542018-09-17 22:56:37 -0400259 ports := &voltha.Ports{}
260 if device, _ := agent.deviceMgr.getDevice(agent.deviceId); device != nil {
261 for _, port := range device.Ports {
khenaidoo92e62c52018-10-03 14:02:54 -0400262 if port.Type == portType {
khenaidoob9203542018-09-17 22:56:37 -0400263 ports.Items = append(ports.Items, port)
264 }
265 }
266 }
267 return ports
268}
269
khenaidoo4d4802d2018-10-04 21:59:49 -0400270// getSwitchCapability is a helper method that a logical device agent uses to retrieve the switch capability of a
271// parent device
khenaidoob9203542018-09-17 22:56:37 -0400272func (agent *DeviceAgent) getSwitchCapability(ctx context.Context) (*core_adapter.SwitchCapability, error) {
273 log.Debugw("getSwitchCapability", log.Fields{"deviceId": agent.deviceId})
274 if device, err := agent.deviceMgr.getDevice(agent.deviceId); device == nil {
275 return nil, err
276 } else {
277 var switchCap *core_adapter.SwitchCapability
278 var err error
279 if switchCap, err = agent.adapterProxy.GetOfpDeviceInfo(ctx, device); err != nil {
280 log.Debugw("getSwitchCapability-error", log.Fields{"id": device.Id, "error": err})
281 return nil, err
282 }
283 return switchCap, nil
284 }
285}
286
khenaidoo4d4802d2018-10-04 21:59:49 -0400287// getPortCapability is a helper method that a logical device agent uses to retrieve the port capability of a
288// device
khenaidoob9203542018-09-17 22:56:37 -0400289func (agent *DeviceAgent) getPortCapability(ctx context.Context, portNo uint32) (*core_adapter.PortCapability, error) {
290 log.Debugw("getPortCapability", log.Fields{"deviceId": agent.deviceId})
291 if device, err := agent.deviceMgr.getDevice(agent.deviceId); device == nil {
292 return nil, err
293 } else {
294 var portCap *core_adapter.PortCapability
295 var err error
296 if portCap, err = agent.adapterProxy.GetOfpPortInfo(ctx, device, portNo); err != nil {
297 log.Debugw("getPortCapability-error", log.Fields{"id": device.Id, "error": err})
298 return nil, err
299 }
300 return portCap, nil
301 }
302}
303
khenaidoo4d4802d2018-10-04 21:59:49 -0400304// TODO: implement when callback from the data model is ready
305// processUpdate is a callback invoked whenever there is a change on the device manages by this device agent
khenaidoo92e62c52018-10-03 14:02:54 -0400306func (agent *DeviceAgent) processUpdate(args ...interface{}) interface{} {
307 log.Debug("!!!!!!!!!!!!!!!!!!!!!!!!!")
308 log.Debugw("processUpdate", log.Fields{"deviceId": agent.deviceId, "args": args})
309 return nil
310}
311
khenaidoob9203542018-09-17 22:56:37 -0400312func (agent *DeviceAgent) updateDevice(device *voltha.Device) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400313 agent.lockDevice.Lock()
314 //defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400315 log.Debugw("updateDevice", log.Fields{"deviceId": device.Id})
316 // Get the dev info from the model
khenaidoo92e62c52018-10-03 14:02:54 -0400317 if storedData, err := agent.getDeviceWithoutLock(); err != nil {
318 agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400319 return status.Errorf(codes.NotFound, "%s", device.Id)
320 } else {
321 // store the changed data
khenaidoo92e62c52018-10-03 14:02:54 -0400322 cloned := proto.Clone(device).(*voltha.Device)
khenaidoo9a468962018-09-19 15:33:13 -0400323 afterUpdate := agent.clusterDataProxy.Update("/devices/"+device.Id, cloned, false, "")
khenaidoo92e62c52018-10-03 14:02:54 -0400324 agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400325 if afterUpdate == nil {
326 return status.Errorf(codes.Internal, "%s", device.Id)
327 }
328 // Perform the state transition
329 if err := agent.deviceMgr.processTransition(storedData, cloned); err != nil {
330 log.Warnw("process-transition-error", log.Fields{"deviceid": device.Id, "error": err})
331 return err
332 }
333 return nil
334 }
335}
336
khenaidoo92e62c52018-10-03 14:02:54 -0400337func (agent *DeviceAgent) updateDeviceStatus(operStatus voltha.OperStatus_OperStatus, connStatus voltha.ConnectStatus_ConnectStatus) error {
338 agent.lockDevice.Lock()
339 //defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400340 // Work only on latest data
khenaidoo92e62c52018-10-03 14:02:54 -0400341 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
342 agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400343 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
344 } else {
345 // clone the device
khenaidoo92e62c52018-10-03 14:02:54 -0400346 cloned := proto.Clone(storeDevice).(*voltha.Device)
347 // Ensure the enums passed in are valid - they will be invalid if they are not set when this function is invoked
348 if s, ok := voltha.ConnectStatus_ConnectStatus_value[connStatus.String()]; ok {
349 log.Debugw("updateDeviceStatus-conn", log.Fields{"ok": ok, "val": s})
350 cloned.ConnectStatus = connStatus
khenaidoob9203542018-09-17 22:56:37 -0400351 }
khenaidoo92e62c52018-10-03 14:02:54 -0400352 if s, ok := voltha.OperStatus_OperStatus_value[operStatus.String()]; ok {
353 log.Debugw("updateDeviceStatus-oper", log.Fields{"ok": ok, "val": s})
354 cloned.OperStatus = operStatus
khenaidoob9203542018-09-17 22:56:37 -0400355 }
khenaidoo92e62c52018-10-03 14:02:54 -0400356 log.Debugw("updateDeviceStatus", log.Fields{"deviceId": cloned.Id, "operStatus": cloned.OperStatus, "connectStatus": cloned.ConnectStatus})
khenaidoob9203542018-09-17 22:56:37 -0400357 // Store the device
khenaidoo92e62c52018-10-03 14:02:54 -0400358 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
359 agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400360 return status.Errorf(codes.Internal, "%s", agent.deviceId)
361 }
khenaidoo92e62c52018-10-03 14:02:54 -0400362 agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400363 // Perform the state transition
khenaidoo92e62c52018-10-03 14:02:54 -0400364 if err := agent.deviceMgr.processTransition(storeDevice, cloned); err != nil {
365 log.Warnw("process-transition-error", log.Fields{"deviceid": agent.deviceId, "error": err})
366 return err
367 }
368 return nil
369 }
370}
371
372func (agent *DeviceAgent) updatePortState(portType voltha.Port_PortType, portNo uint32, operStatus voltha.OperStatus_OperStatus) error {
373 agent.lockDevice.Lock()
374 //defer agent.lockDevice.Unlock()
375 // Work only on latest data
376 // TODO: Get list of ports from device directly instead of the entire device
377 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
378 agent.lockDevice.Unlock()
379 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
380 } else {
381 // clone the device
382 cloned := proto.Clone(storeDevice).(*voltha.Device)
383 // Ensure the enums passed in are valid - they will be invalid if they are not set when this function is invoked
384 if _, ok := voltha.Port_PortType_value[portType.String()]; !ok {
385 agent.lockDevice.Unlock()
386 return status.Errorf(codes.InvalidArgument, "%s", portType)
387 }
388 for _, port := range cloned.Ports {
389 if port.Type == portType && port.PortNo == portNo {
390 port.OperStatus = operStatus
391 // Set the admin status to ENABLED if the operational status is ACTIVE
392 // TODO: Set by northbound system?
393 if operStatus == voltha.OperStatus_ACTIVE {
394 port.AdminState = voltha.AdminState_ENABLED
395 }
396 break
397 }
398 }
399 log.Debugw("portStatusUpdate", log.Fields{"deviceId": cloned.Id})
400 // Store the device
401 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
402 agent.lockDevice.Unlock()
403 return status.Errorf(codes.Internal, "%s", agent.deviceId)
404 }
405 agent.lockDevice.Unlock()
406 // Perform the state transition
407 if err := agent.deviceMgr.processTransition(storeDevice, cloned); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400408 log.Warnw("process-transition-error", log.Fields{"deviceid": agent.deviceId, "error": err})
409 return err
410 }
411 return nil
412 }
413}
414
415func (agent *DeviceAgent) updatePmConfigs(pmConfigs *voltha.PmConfigs) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400416 agent.lockDevice.Lock()
417 defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400418 log.Debug("updatePmConfigs")
419 // Work only on latest data
khenaidoo92e62c52018-10-03 14:02:54 -0400420 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400421 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
422 } else {
423 // clone the device
khenaidoo92e62c52018-10-03 14:02:54 -0400424 cloned := proto.Clone(storeDevice).(*voltha.Device)
425 cloned.PmConfigs = proto.Clone(pmConfigs).(*voltha.PmConfigs)
khenaidoob9203542018-09-17 22:56:37 -0400426 // Store the device
khenaidoo92e62c52018-10-03 14:02:54 -0400427 afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, "")
khenaidoob9203542018-09-17 22:56:37 -0400428 if afterUpdate == nil {
429 return status.Errorf(codes.Internal, "%s", agent.deviceId)
430 }
431 return nil
432 }
433}
434
435func (agent *DeviceAgent) addPort(port *voltha.Port) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400436 agent.lockDevice.Lock()
437 defer agent.lockDevice.Unlock()
438 log.Debugw("addPort", log.Fields{"deviceId": agent.deviceId})
khenaidoob9203542018-09-17 22:56:37 -0400439 // Work only on latest data
khenaidoo92e62c52018-10-03 14:02:54 -0400440 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400441 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
442 } else {
443 // clone the device
khenaidoo92e62c52018-10-03 14:02:54 -0400444 cloned := proto.Clone(storeDevice).(*voltha.Device)
khenaidoob9203542018-09-17 22:56:37 -0400445 if cloned.Ports == nil {
446 // First port
khenaidoo92e62c52018-10-03 14:02:54 -0400447 log.Debugw("addPort-first-port-to-add", log.Fields{"deviceId": agent.deviceId})
khenaidoob9203542018-09-17 22:56:37 -0400448 cloned.Ports = make([]*voltha.Port, 0)
449 }
khenaidoo92e62c52018-10-03 14:02:54 -0400450 cp := proto.Clone(port).(*voltha.Port)
451 // Set the admin state of the port to ENABLE if the operational state is ACTIVE
452 // TODO: Set by northbound system?
453 if cp.OperStatus == voltha.OperStatus_ACTIVE {
454 cp.AdminState = voltha.AdminState_ENABLED
455 }
456 cloned.Ports = append(cloned.Ports, cp)
khenaidoob9203542018-09-17 22:56:37 -0400457 // Store the device
khenaidoo92e62c52018-10-03 14:02:54 -0400458 afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, "")
459 if afterUpdate == nil {
460 return status.Errorf(codes.Internal, "%s", agent.deviceId)
461 }
462 return nil
463 }
464}
465
466func (agent *DeviceAgent) addPeerPort(port *voltha.Port_PeerPort) error {
467 agent.lockDevice.Lock()
468 defer agent.lockDevice.Unlock()
469 log.Debug("addPeerPort")
470 // Work only on latest data
471 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
472 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
473 } else {
474 // clone the device
475 cloned := proto.Clone(storeDevice).(*voltha.Device)
476 // Get the peer port on the device based on the port no
477 for _, peerPort := range cloned.Ports {
478 if peerPort.PortNo == port.PortNo { // found port
479 cp := proto.Clone(port).(*voltha.Port_PeerPort)
480 peerPort.Peers = append(peerPort.Peers, cp)
481 log.Debugw("found-peer", log.Fields{"portNo": port.PortNo, "deviceId": agent.deviceId})
482 break
483 }
484 }
khenaidoo4d4802d2018-10-04 21:59:49 -0400485 //To track an issue when adding peer-port.
486 log.Debugw("before-peer-added", log.Fields{"device": cloned})
khenaidoo92e62c52018-10-03 14:02:54 -0400487 // Store the device
488 afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, "")
khenaidoob9203542018-09-17 22:56:37 -0400489 if afterUpdate == nil {
490 return status.Errorf(codes.Internal, "%s", agent.deviceId)
491 }
khenaidoo4d4802d2018-10-04 21:59:49 -0400492 //To track an issue when adding peer-port.
493 if d, ok := afterUpdate.(*voltha.Device); ok {
494 log.Debugw("after-peer-added", log.Fields{"device": d})
495 } else {
496 log.Debug("after-peer-added-incorrect-type", log.Fields{"type": reflect.ValueOf(afterUpdate).Type()})
497 }
498
khenaidoob9203542018-09-17 22:56:37 -0400499 return nil
500 }
501}
502
503// TODO: A generic device update by attribute
504func (agent *DeviceAgent) updateDeviceAttribute(name string, value interface{}) {
khenaidoo92e62c52018-10-03 14:02:54 -0400505 agent.lockDevice.Lock()
506 defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400507 if value == nil {
508 return
509 }
510 var storeDevice *voltha.Device
511 var err error
khenaidoo92e62c52018-10-03 14:02:54 -0400512 if storeDevice, err = agent.getDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400513 return
514 }
515 updated := false
516 s := reflect.ValueOf(storeDevice).Elem()
517 if s.Kind() == reflect.Struct {
518 // exported field
519 f := s.FieldByName(name)
520 if f.IsValid() && f.CanSet() {
521 switch f.Kind() {
522 case reflect.String:
523 f.SetString(value.(string))
524 updated = true
525 case reflect.Uint32:
526 f.SetUint(uint64(value.(uint32)))
527 updated = true
528 case reflect.Bool:
529 f.SetBool(value.(bool))
530 updated = true
531 }
532 }
533 }
khenaidoo92e62c52018-10-03 14:02:54 -0400534 log.Debugw("update-field-status", log.Fields{"deviceId": storeDevice.Id, "name": name, "updated": updated})
khenaidoob9203542018-09-17 22:56:37 -0400535 // Save the data
khenaidoo92e62c52018-10-03 14:02:54 -0400536 cloned := proto.Clone(storeDevice).(*voltha.Device)
537 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
khenaidoob9203542018-09-17 22:56:37 -0400538 log.Warnw("attribute-update-failed", log.Fields{"attribute": name, "value": value})
539 }
540 return
541}