blob: d00375fc082abe02ea68ca8bc37e0109fa8b5f57 [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"
khenaidoo19d7b632018-10-30 10:49:50 -040020 "fmt"
khenaidoob9203542018-09-17 22:56:37 -040021 "github.com/gogo/protobuf/proto"
22 "github.com/opencord/voltha-go/common/log"
23 "github.com/opencord/voltha-go/db/model"
William Kurkiandaa6bb22019-03-07 12:26:28 -050024 ic "github.com/opencord/voltha-protos/go/inter_container"
25 ofp "github.com/opencord/voltha-protos/go/openflow_13"
26 "github.com/opencord/voltha-protos/go/voltha"
khenaidoo19d7b632018-10-30 10:49:50 -040027 fu "github.com/opencord/voltha-go/rw_core/utils"
khenaidoob9203542018-09-17 22:56:37 -040028 "google.golang.org/grpc/codes"
29 "google.golang.org/grpc/status"
khenaidoo19d7b632018-10-30 10:49:50 -040030 "reflect"
31 "sync"
khenaidoob9203542018-09-17 22:56:37 -040032)
33
34type DeviceAgent struct {
khenaidoo9a468962018-09-19 15:33:13 -040035 deviceId string
khenaidoo43c82122018-11-22 18:38:28 -050036 deviceType string
khenaidoo9a468962018-09-19 15:33:13 -040037 lastData *voltha.Device
38 adapterProxy *AdapterProxy
khenaidoo21d51152019-02-01 13:48:37 -050039 adapterMgr *AdapterManager
khenaidoo9a468962018-09-19 15:33:13 -040040 deviceMgr *DeviceManager
41 clusterDataProxy *model.Proxy
khenaidoo92e62c52018-10-03 14:02:54 -040042 deviceProxy *model.Proxy
khenaidoo9a468962018-09-19 15:33:13 -040043 exitChannel chan int
khenaidoo19d7b632018-10-30 10:49:50 -040044 flowProxy *model.Proxy
45 groupProxy *model.Proxy
khenaidoo92e62c52018-10-03 14:02:54 -040046 lockDevice sync.RWMutex
khenaidoob9203542018-09-17 22:56:37 -040047}
48
khenaidoo4d4802d2018-10-04 21:59:49 -040049//newDeviceAgent creates a new device agent along as creating a unique ID for the device and set the device state to
50//preprovisioning
khenaidoo9a468962018-09-19 15:33:13 -040051func newDeviceAgent(ap *AdapterProxy, device *voltha.Device, deviceMgr *DeviceManager, cdProxy *model.Proxy) *DeviceAgent {
khenaidoob9203542018-09-17 22:56:37 -040052 var agent DeviceAgent
khenaidoob9203542018-09-17 22:56:37 -040053 agent.adapterProxy = ap
khenaidoo92e62c52018-10-03 14:02:54 -040054 cloned := (proto.Clone(device)).(*voltha.Device)
Stephane Barbarie1ab43272018-12-08 21:42:13 -050055 if cloned.Id == "" {
56 cloned.Id = CreateDeviceId()
khenaidoo297cd252019-02-07 22:10:23 -050057 cloned.AdminState = voltha.AdminState_PREPROVISIONED
58 cloned.FlowGroups = &ofp.FlowGroups{Items: nil}
59 cloned.Flows = &ofp.Flows{Items: nil}
Stephane Barbarie1ab43272018-12-08 21:42:13 -050060 }
khenaidoo19d7b632018-10-30 10:49:50 -040061 if !device.GetRoot() && device.ProxyAddress != nil {
62 // Set the default vlan ID to the one specified by the parent adapter. It can be
63 // overwritten by the child adapter during a device update request
64 cloned.Vlan = device.ProxyAddress.ChannelId
65 }
khenaidoo92e62c52018-10-03 14:02:54 -040066 agent.deviceId = cloned.Id
khenaidoofdbad6e2018-11-06 22:26:38 -050067 agent.deviceType = cloned.Type
khenaidoo92e62c52018-10-03 14:02:54 -040068 agent.lastData = cloned
khenaidoob9203542018-09-17 22:56:37 -040069 agent.deviceMgr = deviceMgr
khenaidoo21d51152019-02-01 13:48:37 -050070 agent.adapterMgr = deviceMgr.adapterMgr
khenaidoob9203542018-09-17 22:56:37 -040071 agent.exitChannel = make(chan int, 1)
khenaidoo9a468962018-09-19 15:33:13 -040072 agent.clusterDataProxy = cdProxy
khenaidoo92e62c52018-10-03 14:02:54 -040073 agent.lockDevice = sync.RWMutex{}
khenaidoob9203542018-09-17 22:56:37 -040074 return &agent
75}
76
khenaidoo297cd252019-02-07 22:10:23 -050077// start save the device to the data model and registers for callbacks on that device if loadFromdB is false. Otherwise,
78// it will load the data from the dB and setup teh necessary callbacks and proxies.
79func (agent *DeviceAgent) start(ctx context.Context, loadFromdB bool) error {
khenaidoo92e62c52018-10-03 14:02:54 -040080 agent.lockDevice.Lock()
81 defer agent.lockDevice.Unlock()
khenaidoo297cd252019-02-07 22:10:23 -050082 log.Debugw("starting-device-agent", log.Fields{"deviceId": agent.deviceId})
83 if loadFromdB {
84 if device := agent.clusterDataProxy.Get("/devices/"+agent.deviceId, 1, false, ""); device != nil {
85 if d, ok := device.(*voltha.Device); ok {
86 agent.lastData = proto.Clone(d).(*voltha.Device)
khenaidoo6d055132019-02-12 16:51:19 -050087 agent.deviceType = agent.lastData.Adapter
khenaidoo297cd252019-02-07 22:10:23 -050088 }
89 } else {
90 log.Errorw("failed-to-load-device", log.Fields{"deviceId": agent.deviceId})
91 return status.Errorf(codes.NotFound, "device-%s", agent.deviceId)
92 }
93 log.Debugw("device-loaded-from-dB", log.Fields{"device": agent.lastData})
94 } else {
95 // Add the initial device to the local model
96 if added := agent.clusterDataProxy.AddWithID("/devices", agent.deviceId, agent.lastData, ""); added == nil {
97 log.Errorw("failed-to-add-device", log.Fields{"deviceId": agent.deviceId})
98 }
khenaidoob9203542018-09-17 22:56:37 -040099 }
khenaidoo297cd252019-02-07 22:10:23 -0500100
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400101 agent.deviceProxy = agent.clusterDataProxy.CreateProxy("/devices/"+agent.deviceId, false)
khenaidoo43c82122018-11-22 18:38:28 -0500102 agent.deviceProxy.RegisterCallback(model.POST_UPDATE, agent.processUpdate)
khenaidoo19d7b632018-10-30 10:49:50 -0400103
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400104 agent.flowProxy = agent.clusterDataProxy.CreateProxy(
khenaidoo19d7b632018-10-30 10:49:50 -0400105 fmt.Sprintf("/devices/%s/flows", agent.deviceId),
106 false)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400107 agent.groupProxy = agent.clusterDataProxy.CreateProxy(
khenaidoo19d7b632018-10-30 10:49:50 -0400108 fmt.Sprintf("/devices/%s/flow_groups", agent.deviceId),
109 false)
110
111 agent.flowProxy.RegisterCallback(model.POST_UPDATE, agent.flowTableUpdated)
khenaidoo43c82122018-11-22 18:38:28 -0500112 agent.groupProxy.RegisterCallback(model.POST_UPDATE, agent.groupTableUpdated)
khenaidoo19d7b632018-10-30 10:49:50 -0400113
khenaidoob9203542018-09-17 22:56:37 -0400114 log.Debug("device-agent-started")
khenaidoo297cd252019-02-07 22:10:23 -0500115 return nil
khenaidoob9203542018-09-17 22:56:37 -0400116}
117
khenaidoo4d4802d2018-10-04 21:59:49 -0400118// stop stops the device agent. Not much to do for now
119func (agent *DeviceAgent) stop(ctx context.Context) {
khenaidoo92e62c52018-10-03 14:02:54 -0400120 agent.lockDevice.Lock()
121 defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400122 log.Debug("stopping-device-agent")
123 agent.exitChannel <- 1
124 log.Debug("device-agent-stopped")
125}
126
khenaidoo19d7b632018-10-30 10:49:50 -0400127// GetDevice retrieves the latest device information from the data model
khenaidoo92e62c52018-10-03 14:02:54 -0400128func (agent *DeviceAgent) getDevice() (*voltha.Device, error) {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400129 agent.lockDevice.RLock()
130 defer agent.lockDevice.RUnlock()
khenaidoo297cd252019-02-07 22:10:23 -0500131 if device := agent.clusterDataProxy.Get("/devices/"+agent.deviceId, 0, false, ""); device != nil {
khenaidoo92e62c52018-10-03 14:02:54 -0400132 if d, ok := device.(*voltha.Device); ok {
133 cloned := proto.Clone(d).(*voltha.Device)
134 return cloned, nil
135 }
136 }
137 return nil, status.Errorf(codes.NotFound, "device-%s", agent.deviceId)
138}
139
khenaidoo4d4802d2018-10-04 21:59:49 -0400140// 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 -0400141// This function is meant so that we do not have duplicate code all over the device agent functions
142func (agent *DeviceAgent) getDeviceWithoutLock() (*voltha.Device, error) {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400143 if device := agent.clusterDataProxy.Get("/devices/"+agent.deviceId, 0, false, ""); device != nil {
khenaidoo92e62c52018-10-03 14:02:54 -0400144 if d, ok := device.(*voltha.Device); ok {
145 cloned := proto.Clone(d).(*voltha.Device)
146 return cloned, nil
147 }
148 }
149 return nil, status.Errorf(codes.NotFound, "device-%s", agent.deviceId)
150}
151
khenaidoo4d4802d2018-10-04 21:59:49 -0400152// enableDevice activates a preprovisioned or disable device
khenaidoob9203542018-09-17 22:56:37 -0400153func (agent *DeviceAgent) enableDevice(ctx context.Context) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400154 agent.lockDevice.Lock()
155 defer agent.lockDevice.Unlock()
156 log.Debugw("enableDevice", log.Fields{"id": agent.deviceId})
khenaidoo21d51152019-02-01 13:48:37 -0500157
khenaidoo92e62c52018-10-03 14:02:54 -0400158 if device, err := agent.getDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400159 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
160 } else {
khenaidoo21d51152019-02-01 13:48:37 -0500161 // First figure out which adapter will handle this device type. We do it at this stage as allow devices to be
162 // pre-provisionned with the required adapter not registered. At this stage, since we need to communicate
163 // with the adapter then we need to know the adapter that will handle this request
164 if adapterName, err := agent.adapterMgr.getAdapterName(device.Type); err != nil {
165 log.Warnw("no-adapter-registered-for-device-type", log.Fields{"deviceType": device.Type, "deviceAdapter": device.Adapter})
166 return err
167 } else {
168 device.Adapter = adapterName
169 }
170
khenaidoo92e62c52018-10-03 14:02:54 -0400171 if device.AdminState == voltha.AdminState_ENABLED {
172 log.Debugw("device-already-enabled", log.Fields{"id": agent.deviceId})
173 //TODO: Needs customized error message
174 return nil
175 }
khenaidoo4d4802d2018-10-04 21:59:49 -0400176 //TODO: if parent device is disabled then do not enable device
khenaidoo92e62c52018-10-03 14:02:54 -0400177 // Verify whether we need to adopt the device the first time
178 // TODO: A state machine for these state transitions would be better (we just have to handle
179 // a limited set of states now or it may be an overkill)
180 if device.AdminState == voltha.AdminState_PREPROVISIONED {
181 // First send the request to an Adapter and wait for a response
182 if err := agent.adapterProxy.AdoptDevice(ctx, device); err != nil {
183 log.Debugw("adoptDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
khenaidoob9203542018-09-17 22:56:37 -0400184 return err
185 }
khenaidoo92e62c52018-10-03 14:02:54 -0400186 } else {
187 // First send the request to an Adapter and wait for a response
188 if err := agent.adapterProxy.ReEnableDevice(ctx, device); err != nil {
189 log.Debugw("renableDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
190 return err
191 }
192 }
193 // Received an Ack (no error found above). Now update the device in the model to the expected state
194 cloned := proto.Clone(device).(*voltha.Device)
195 cloned.AdminState = voltha.AdminState_ENABLED
196 cloned.OperStatus = voltha.OperStatus_ACTIVATING
197 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
198 return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
khenaidoob9203542018-09-17 22:56:37 -0400199 }
200 }
201 return nil
202}
203
khenaidoo19d7b632018-10-30 10:49:50 -0400204func (agent *DeviceAgent) updateFlows(flows []*ofp.OfpFlowStats) error {
205 agent.lockDevice.Lock()
206 defer agent.lockDevice.Unlock()
207 log.Debugw("updateFlows", log.Fields{"deviceId": agent.deviceId, "flows": flows})
208 var oldData *voltha.Flows
209 if storedData, err := agent.getDeviceWithoutLock(); err != nil {
210 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
211 } else {
212 oldData = proto.Clone(storedData.Flows).(*voltha.Flows)
213 log.Debugw("updateFlows", log.Fields{"deviceId": agent.deviceId, "flows": flows, "old": oldData})
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500214
khenaidoo19d7b632018-10-30 10:49:50 -0400215 // store the changed data
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500216 afterUpdate := agent.flowProxy.Update("/", &ofp.Flows{Items: flows}, false, "")
khenaidoo19d7b632018-10-30 10:49:50 -0400217 if afterUpdate == nil {
218 return status.Errorf(codes.Internal, "%s", agent.deviceId)
219 }
220
khenaidoo19d7b632018-10-30 10:49:50 -0400221 return nil
222 }
223}
224
225func (agent *DeviceAgent) updateGroups(groups []*ofp.OfpGroupEntry) error {
226 agent.lockDevice.Lock()
227 defer agent.lockDevice.Unlock()
khenaidoo19d7b632018-10-30 10:49:50 -0400228 log.Debugw("updateGroups", log.Fields{"deviceId": agent.deviceId, "groups": groups})
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500229 if _, err := agent.getDeviceWithoutLock(); err != nil {
khenaidoo19d7b632018-10-30 10:49:50 -0400230 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
231 } else {
khenaidoo19d7b632018-10-30 10:49:50 -0400232 // store the changed data
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500233 afterUpdate := agent.groupProxy.Update("/", &ofp.FlowGroups{Items: groups}, false, "")
khenaidoo19d7b632018-10-30 10:49:50 -0400234 if afterUpdate == nil {
235 return status.Errorf(codes.Internal, "%s", agent.deviceId)
236 }
237
khenaidoo19d7b632018-10-30 10:49:50 -0400238 return nil
239 }
240}
241
khenaidoo4d4802d2018-10-04 21:59:49 -0400242//disableDevice disable a device
khenaidoo92e62c52018-10-03 14:02:54 -0400243func (agent *DeviceAgent) disableDevice(ctx context.Context) error {
244 agent.lockDevice.Lock()
245 //defer agent.lockDevice.Unlock()
246 log.Debugw("disableDevice", log.Fields{"id": agent.deviceId})
247 // Get the most up to date the device info
248 if device, err := agent.getDeviceWithoutLock(); err != nil {
249 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
250 } else {
251 if device.AdminState == voltha.AdminState_DISABLED {
252 log.Debugw("device-already-disabled", log.Fields{"id": agent.deviceId})
253 //TODO: Needs customized error message
254 agent.lockDevice.Unlock()
255 return nil
256 }
257 // First send the request to an Adapter and wait for a response
258 if err := agent.adapterProxy.DisableDevice(ctx, device); err != nil {
259 log.Debugw("disableDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
260 agent.lockDevice.Unlock()
261 return err
262 }
263 // Received an Ack (no error found above). Now update the device in the model to the expected state
264 cloned := proto.Clone(device).(*voltha.Device)
265 cloned.AdminState = voltha.AdminState_DISABLED
266 // Set the state of all ports on that device to disable
267 for _, port := range cloned.Ports {
268 port.AdminState = voltha.AdminState_DISABLED
269 port.OperStatus = voltha.OperStatus_UNKNOWN
270 }
271 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
272 agent.lockDevice.Unlock()
273 return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
274 }
275 agent.lockDevice.Unlock()
khenaidoo92e62c52018-10-03 14:02:54 -0400276 }
277 return nil
278}
279
khenaidoo4d4802d2018-10-04 21:59:49 -0400280func (agent *DeviceAgent) rebootDevice(ctx context.Context) error {
281 agent.lockDevice.Lock()
282 defer agent.lockDevice.Unlock()
283 log.Debugw("rebootDevice", log.Fields{"id": agent.deviceId})
284 // Get the most up to date the device info
285 if device, err := agent.getDeviceWithoutLock(); err != nil {
286 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
287 } else {
288 if device.AdminState != voltha.AdminState_DISABLED {
289 log.Debugw("device-not-disabled", log.Fields{"id": agent.deviceId})
290 //TODO: Needs customized error message
291 return status.Errorf(codes.FailedPrecondition, "deviceId:%s, expected-admin-state:%s", agent.deviceId, voltha.AdminState_DISABLED)
292 }
293 // First send the request to an Adapter and wait for a response
294 if err := agent.adapterProxy.RebootDevice(ctx, device); err != nil {
295 log.Debugw("rebootDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
296 return err
297 }
298 }
299 return nil
300}
301
302func (agent *DeviceAgent) deleteDevice(ctx context.Context) error {
303 agent.lockDevice.Lock()
304 log.Debugw("deleteDevice", log.Fields{"id": agent.deviceId})
305 // Get the most up to date the device info
306 if device, err := agent.getDeviceWithoutLock(); err != nil {
307 agent.lockDevice.Unlock()
308 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
309 } else {
khenaidoo43c82122018-11-22 18:38:28 -0500310 if (device.AdminState != voltha.AdminState_DISABLED) &&
311 (device.AdminState != voltha.AdminState_PREPROVISIONED) {
khenaidoo4d4802d2018-10-04 21:59:49 -0400312 log.Debugw("device-not-disabled", log.Fields{"id": agent.deviceId})
313 //TODO: Needs customized error message
314 agent.lockDevice.Unlock()
315 return status.Errorf(codes.FailedPrecondition, "deviceId:%s, expected-admin-state:%s", agent.deviceId, voltha.AdminState_DISABLED)
316 }
khenaidoo7ccedd52018-12-14 16:48:54 -0500317 if device.AdminState != voltha.AdminState_PREPROVISIONED {
318 // Send the request to an Adapter and wait for a response
319 if err := agent.adapterProxy.DeleteDevice(ctx, device); err != nil {
320 log.Debugw("deleteDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
321 agent.lockDevice.Unlock()
322 return err
323 }
khenaidoo4d4802d2018-10-04 21:59:49 -0400324 }
khenaidoo7ccedd52018-12-14 16:48:54 -0500325 if removed := agent.clusterDataProxy.Remove("/devices/"+agent.deviceId, ""); removed == nil {
khenaidoo4d4802d2018-10-04 21:59:49 -0400326 agent.lockDevice.Unlock()
327 return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
328 }
329 agent.lockDevice.Unlock()
khenaidoo4d4802d2018-10-04 21:59:49 -0400330 }
331 return nil
332}
333
khenaidoof5a5bfa2019-01-23 22:20:29 -0500334func (agent *DeviceAgent) downloadImage(ctx context.Context, img *voltha.ImageDownload) (*voltha.OperationResp, error) {
335 agent.lockDevice.Lock()
336 defer agent.lockDevice.Unlock()
337 log.Debugw("downloadImage", log.Fields{"id": agent.deviceId})
338 // Get the most up to date the device info
339 if device, err := agent.getDeviceWithoutLock(); err != nil {
340 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
341 } else {
342 if device.AdminState != voltha.AdminState_ENABLED {
343 log.Debugw("device-not-enabled", log.Fields{"id": agent.deviceId})
344 return nil, status.Errorf(codes.FailedPrecondition, "deviceId:%s, expected-admin-state:%s", agent.deviceId, voltha.AdminState_ENABLED)
345 }
346 // Save the image
347 clonedImg := proto.Clone(img).(*voltha.ImageDownload)
Stephane Barbariedf5479f2019-01-29 22:13:00 -0500348 clonedImg.DownloadState = voltha.ImageDownload_DOWNLOAD_REQUESTED
khenaidoof5a5bfa2019-01-23 22:20:29 -0500349 cloned := proto.Clone(device).(*voltha.Device)
350 if cloned.ImageDownloads == nil {
351 cloned.ImageDownloads = []*voltha.ImageDownload{clonedImg}
352 } else {
353 cloned.ImageDownloads = append(cloned.ImageDownloads, clonedImg)
354 }
355 cloned.AdminState = voltha.AdminState_DOWNLOADING_IMAGE
356 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
357 return nil, status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
358 }
359 // Send the request to the adapter
360 if err := agent.adapterProxy.DownloadImage(ctx, cloned, clonedImg); err != nil {
361 log.Debugw("downloadImage-error", log.Fields{"id": agent.lastData.Id, "error": err, "image": img.Name})
362 return nil, err
363 }
364 }
365 return &voltha.OperationResp{Code: voltha.OperationResp_OPERATION_SUCCESS}, nil
366}
367
368// isImageRegistered is a helper method to figure out if an image is already registered
369func isImageRegistered(img *voltha.ImageDownload, device *voltha.Device) bool {
370 for _, image := range device.ImageDownloads {
371 if image.Id == img.Id && image.Name == img.Name {
372 return true
373 }
374 }
375 return false
376}
377
378func (agent *DeviceAgent) cancelImageDownload(ctx context.Context, img *voltha.ImageDownload) (*voltha.OperationResp, error) {
379 agent.lockDevice.Lock()
380 defer agent.lockDevice.Unlock()
381 log.Debugw("cancelImageDownload", log.Fields{"id": agent.deviceId})
382 // Get the most up to date the device info
383 if device, err := agent.getDeviceWithoutLock(); err != nil {
384 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
385 } else {
386 // Verify whether the Image is in the list of image being downloaded
387 if !isImageRegistered(img, device) {
388 return nil, status.Errorf(codes.FailedPrecondition, "deviceId:%s, image-not-registered:%s", agent.deviceId, img.Name)
389 }
390
391 // Update image download state
392 cloned := proto.Clone(device).(*voltha.Device)
393 for _, image := range cloned.ImageDownloads {
394 if image.Id == img.Id && image.Name == img.Name {
Stephane Barbariedf5479f2019-01-29 22:13:00 -0500395 image.DownloadState = voltha.ImageDownload_DOWNLOAD_CANCELLED
khenaidoof5a5bfa2019-01-23 22:20:29 -0500396 }
397 }
398
399 //If device is in downloading state, send the request to cancel the download
400 if device.AdminState == voltha.AdminState_DOWNLOADING_IMAGE {
401 if err := agent.adapterProxy.CancelImageDownload(ctx, device, img); err != nil {
402 log.Debugw("cancelImageDownload-error", log.Fields{"id": agent.lastData.Id, "error": err, "image": img.Name})
403 return nil, err
404 }
405 // Set the device to Enabled
406 cloned.AdminState = voltha.AdminState_ENABLED
407 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
408 return nil, status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
409 }
410 }
411 }
412 return &voltha.OperationResp{Code: voltha.OperationResp_OPERATION_SUCCESS}, nil
413 }
414
415func (agent *DeviceAgent) activateImage(ctx context.Context, img *voltha.ImageDownload) (*voltha.OperationResp, error) {
416 agent.lockDevice.Lock()
417 defer agent.lockDevice.Unlock()
418 log.Debugw("activateImage", log.Fields{"id": agent.deviceId})
419 // Get the most up to date the device info
420 if device, err := agent.getDeviceWithoutLock(); err != nil {
421 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
422 } else {
423 // Verify whether the Image is in the list of image being downloaded
424 if !isImageRegistered(img, device) {
425 return nil, status.Errorf(codes.FailedPrecondition, "deviceId:%s, image-not-registered:%s", agent.deviceId, img.Name)
426 }
427
428 if device.AdminState == voltha.AdminState_DOWNLOADING_IMAGE {
429 return nil, status.Errorf(codes.FailedPrecondition, "deviceId:%s, device-in-downloading-state:%s", agent.deviceId, img.Name)
430 }
431 // Update image download state
432 cloned := proto.Clone(device).(*voltha.Device)
433 for _, image := range cloned.ImageDownloads {
434 if image.Id == img.Id && image.Name == img.Name {
435 image.ImageState = voltha.ImageDownload_IMAGE_ACTIVATING
436 }
437 }
438 // Set the device to downloading_image
439 cloned.AdminState = voltha.AdminState_DOWNLOADING_IMAGE
440 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
441 return nil, status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
442 }
443
444 if err := agent.adapterProxy.ActivateImageUpdate(ctx, device, img); err != nil {
445 log.Debugw("activateImage-error", log.Fields{"id": agent.lastData.Id, "error": err, "image": img.Name})
446 return nil, err
447 }
448 // The status of the AdminState will be changed following the update_download_status response from the adapter
449 // The image name will also be removed from the device list
450 }
451 return &voltha.OperationResp{Code: voltha.OperationResp_OPERATION_SUCCESS}, nil}
452
453
454func (agent *DeviceAgent) revertImage(ctx context.Context, img *voltha.ImageDownload) (*voltha.OperationResp, error) {
455 agent.lockDevice.Lock()
456 defer agent.lockDevice.Unlock()
457 log.Debugw("revertImage", log.Fields{"id": agent.deviceId})
458 // Get the most up to date the device info
459 if device, err := agent.getDeviceWithoutLock(); err != nil {
460 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
461 } else {
462 // Verify whether the Image is in the list of image being downloaded
463 if !isImageRegistered(img, device) {
464 return nil, status.Errorf(codes.FailedPrecondition, "deviceId:%s, image-not-registered:%s", agent.deviceId, img.Name)
465 }
466
467 if device.AdminState != voltha.AdminState_ENABLED {
468 return nil, status.Errorf(codes.FailedPrecondition, "deviceId:%s, device-not-enabled-state:%s", agent.deviceId, img.Name)
469 }
470 // Update image download state
471 cloned := proto.Clone(device).(*voltha.Device)
472 for _, image := range cloned.ImageDownloads {
473 if image.Id == img.Id && image.Name == img.Name {
474 image.ImageState = voltha.ImageDownload_IMAGE_REVERTING
475 }
476 }
477 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
478 return nil, status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
479 }
480
481 if err := agent.adapterProxy.RevertImageUpdate(ctx, device, img); err != nil {
482 log.Debugw("revertImage-error", log.Fields{"id": agent.lastData.Id, "error": err, "image": img.Name})
483 return nil, err
484 }
485 }
486 return &voltha.OperationResp{Code: voltha.OperationResp_OPERATION_SUCCESS}, nil
487 }
488
489
490func (agent *DeviceAgent) getImageDownloadStatus(ctx context.Context, img *voltha.ImageDownload) (*voltha.ImageDownload, error) {
491 agent.lockDevice.Lock()
492 defer agent.lockDevice.Unlock()
493 log.Debugw("getImageDownloadStatus", log.Fields{"id": agent.deviceId})
494 // Get the most up to date the device info
495 if device, err := agent.getDeviceWithoutLock(); err != nil {
496 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
497 } else {
498 if resp, err := agent.adapterProxy.GetImageDownloadStatus(ctx, device, img); err != nil {
499 log.Debugw("getImageDownloadStatus-error", log.Fields{"id": agent.lastData.Id, "error": err, "image": img.Name})
500 return nil, err
501 } else {
502 return resp, nil
503 }
504 }
505}
506
507func (agent *DeviceAgent) updateImageDownload(img *voltha.ImageDownload) error{
508 agent.lockDevice.Lock()
509 defer agent.lockDevice.Unlock()
510 log.Debugw("updateImageDownload", log.Fields{"id": agent.deviceId})
511 // Get the most up to date the device info
512 if device, err := agent.getDeviceWithoutLock(); err != nil {
513 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
514 } else {
515 // Update the image as well as remove it if the download was cancelled
516 cloned := proto.Clone(device).(*voltha.Device)
517 clonedImages := make([]*voltha.ImageDownload, len(cloned.ImageDownloads))
518 for _, image := range cloned.ImageDownloads {
519 if image.Id == img.Id && image.Name == img.Name {
Stephane Barbariedf5479f2019-01-29 22:13:00 -0500520 if image.DownloadState != voltha.ImageDownload_DOWNLOAD_CANCELLED {
khenaidoof5a5bfa2019-01-23 22:20:29 -0500521 clonedImages = append(clonedImages, img)
522 }
523 }
524 }
525 cloned.ImageDownloads = clonedImages
526 // Set the Admin state to enabled if required
Stephane Barbariedf5479f2019-01-29 22:13:00 -0500527 if (img.DownloadState != voltha.ImageDownload_DOWNLOAD_REQUESTED &&
528 img.DownloadState != voltha.ImageDownload_DOWNLOAD_STARTED) ||
khenaidoof5a5bfa2019-01-23 22:20:29 -0500529 (img.ImageState != voltha.ImageDownload_IMAGE_ACTIVATING){
530 cloned.AdminState = voltha.AdminState_ENABLED
531 }
532
533 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
534 return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
535 }
536 }
537 return nil
538}
539
540func (agent *DeviceAgent) getImageDownload(ctx context.Context, img *voltha.ImageDownload) (*voltha.ImageDownload, error) {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400541 agent.lockDevice.RLock()
542 defer agent.lockDevice.RUnlock()
khenaidoof5a5bfa2019-01-23 22:20:29 -0500543 log.Debugw("getImageDownload", log.Fields{"id": agent.deviceId})
544 // Get the most up to date the device info
545 if device, err := agent.getDeviceWithoutLock(); err != nil {
546 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
547 } else {
548 for _, image := range device.ImageDownloads {
549 if image.Id == img.Id && image.Name == img.Name {
550 return image, nil
551 }
552 }
553 return nil, status.Errorf(codes.NotFound, "image-not-found:%s", img.Name)
554 }
555}
556
557func (agent *DeviceAgent) listImageDownloads(ctx context.Context, deviceId string) (*voltha.ImageDownloads, error) {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400558 agent.lockDevice.RLock()
559 defer agent.lockDevice.RUnlock()
khenaidoof5a5bfa2019-01-23 22:20:29 -0500560 log.Debugw("listImageDownloads", log.Fields{"id": agent.deviceId})
561 // Get the most up to date the device info
562 if device, err := agent.getDeviceWithoutLock(); err != nil {
563 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
564 } else {
565 return &voltha.ImageDownloads{Items:device.ImageDownloads}, nil
566 }
567}
568
khenaidoo4d4802d2018-10-04 21:59:49 -0400569// getPorts retrieves the ports information of the device based on the port type.
khenaidoo92e62c52018-10-03 14:02:54 -0400570func (agent *DeviceAgent) getPorts(ctx context.Context, portType voltha.Port_PortType) *voltha.Ports {
571 log.Debugw("getPorts", log.Fields{"id": agent.deviceId, "portType": portType})
khenaidoob9203542018-09-17 22:56:37 -0400572 ports := &voltha.Ports{}
khenaidoo19d7b632018-10-30 10:49:50 -0400573 if device, _ := agent.deviceMgr.GetDevice(agent.deviceId); device != nil {
khenaidoob9203542018-09-17 22:56:37 -0400574 for _, port := range device.Ports {
khenaidoo92e62c52018-10-03 14:02:54 -0400575 if port.Type == portType {
khenaidoob9203542018-09-17 22:56:37 -0400576 ports.Items = append(ports.Items, port)
577 }
578 }
579 }
580 return ports
581}
582
khenaidoo4d4802d2018-10-04 21:59:49 -0400583// getSwitchCapability is a helper method that a logical device agent uses to retrieve the switch capability of a
584// parent device
khenaidoo79232702018-12-04 11:00:41 -0500585func (agent *DeviceAgent) getSwitchCapability(ctx context.Context) (*ic.SwitchCapability, error) {
khenaidoob9203542018-09-17 22:56:37 -0400586 log.Debugw("getSwitchCapability", log.Fields{"deviceId": agent.deviceId})
khenaidoo19d7b632018-10-30 10:49:50 -0400587 if device, err := agent.deviceMgr.GetDevice(agent.deviceId); device == nil {
khenaidoob9203542018-09-17 22:56:37 -0400588 return nil, err
589 } else {
khenaidoo79232702018-12-04 11:00:41 -0500590 var switchCap *ic.SwitchCapability
khenaidoob9203542018-09-17 22:56:37 -0400591 var err error
592 if switchCap, err = agent.adapterProxy.GetOfpDeviceInfo(ctx, device); err != nil {
593 log.Debugw("getSwitchCapability-error", log.Fields{"id": device.Id, "error": err})
594 return nil, err
595 }
596 return switchCap, nil
597 }
598}
599
khenaidoo4d4802d2018-10-04 21:59:49 -0400600// getPortCapability is a helper method that a logical device agent uses to retrieve the port capability of a
601// device
khenaidoo79232702018-12-04 11:00:41 -0500602func (agent *DeviceAgent) getPortCapability(ctx context.Context, portNo uint32) (*ic.PortCapability, error) {
khenaidoob9203542018-09-17 22:56:37 -0400603 log.Debugw("getPortCapability", log.Fields{"deviceId": agent.deviceId})
khenaidoo19d7b632018-10-30 10:49:50 -0400604 if device, err := agent.deviceMgr.GetDevice(agent.deviceId); device == nil {
khenaidoob9203542018-09-17 22:56:37 -0400605 return nil, err
606 } else {
khenaidoo79232702018-12-04 11:00:41 -0500607 var portCap *ic.PortCapability
khenaidoob9203542018-09-17 22:56:37 -0400608 var err error
609 if portCap, err = agent.adapterProxy.GetOfpPortInfo(ctx, device, portNo); err != nil {
610 log.Debugw("getPortCapability-error", log.Fields{"id": device.Id, "error": err})
611 return nil, err
612 }
613 return portCap, nil
614 }
615}
616
khenaidoofdbad6e2018-11-06 22:26:38 -0500617func (agent *DeviceAgent) packetOut(outPort uint32, packet *ofp.OfpPacketOut) error {
618 // Send packet to adapter
619 if err := agent.adapterProxy.packetOut(agent.deviceType, agent.deviceId, outPort, packet); err != nil {
620 log.Debugw("packet-out-error", log.Fields{"id": agent.lastData.Id, "error": err})
621 return err
622 }
623 return nil
624}
625
khenaidoo4d4802d2018-10-04 21:59:49 -0400626// processUpdate is a callback invoked whenever there is a change on the device manages by this device agent
khenaidoo92e62c52018-10-03 14:02:54 -0400627func (agent *DeviceAgent) processUpdate(args ...interface{}) interface{} {
khenaidoo43c82122018-11-22 18:38:28 -0500628 //// Run this callback in its own go routine
629 go func(args ...interface{}) interface{} {
630 var previous *voltha.Device
631 var current *voltha.Device
632 var ok bool
633 if len(args) == 2 {
634 if previous, ok = args[0].(*voltha.Device); !ok {
635 log.Errorw("invalid-callback-type", log.Fields{"data": args[0]})
636 return nil
637 }
638 if current, ok = args[1].(*voltha.Device); !ok {
639 log.Errorw("invalid-callback-type", log.Fields{"data": args[1]})
640 return nil
641 }
642 } else {
643 log.Errorw("too-many-args-in-callback", log.Fields{"len": len(args)})
644 return nil
645 }
646 // Perform the state transition in it's own go routine
khenaidoof5a5bfa2019-01-23 22:20:29 -0500647 if err := agent.deviceMgr.processTransition(previous, current); err != nil {
648 log.Errorw("failed-process-transition", log.Fields{"deviceId": previous.Id,
649 "previousAdminState": previous.AdminState, "currentAdminState": current.AdminState})
650 }
khenaidoo43c82122018-11-22 18:38:28 -0500651 return nil
652 }(args...)
653
khenaidoo92e62c52018-10-03 14:02:54 -0400654 return nil
655}
656
khenaidoob9203542018-09-17 22:56:37 -0400657func (agent *DeviceAgent) updateDevice(device *voltha.Device) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400658 agent.lockDevice.Lock()
khenaidoo43c82122018-11-22 18:38:28 -0500659 defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400660 log.Debugw("updateDevice", log.Fields{"deviceId": device.Id})
khenaidoo43c82122018-11-22 18:38:28 -0500661 cloned := proto.Clone(device).(*voltha.Device)
662 afterUpdate := agent.clusterDataProxy.Update("/devices/"+device.Id, cloned, false, "")
663 if afterUpdate == nil {
664 return status.Errorf(codes.Internal, "%s", device.Id)
khenaidoob9203542018-09-17 22:56:37 -0400665 }
khenaidoo43c82122018-11-22 18:38:28 -0500666 return nil
667}
668
669func (agent *DeviceAgent) updateDeviceWithoutLock(device *voltha.Device) error {
670 log.Debugw("updateDevice", log.Fields{"deviceId": device.Id})
671 cloned := proto.Clone(device).(*voltha.Device)
672 afterUpdate := agent.clusterDataProxy.Update("/devices/"+device.Id, cloned, false, "")
673 if afterUpdate == nil {
674 return status.Errorf(codes.Internal, "%s", device.Id)
675 }
676 return nil
khenaidoob9203542018-09-17 22:56:37 -0400677}
678
khenaidoo92e62c52018-10-03 14:02:54 -0400679func (agent *DeviceAgent) updateDeviceStatus(operStatus voltha.OperStatus_OperStatus, connStatus voltha.ConnectStatus_ConnectStatus) error {
680 agent.lockDevice.Lock()
681 //defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400682 // Work only on latest data
khenaidoo92e62c52018-10-03 14:02:54 -0400683 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
684 agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400685 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
686 } else {
687 // clone the device
khenaidoo92e62c52018-10-03 14:02:54 -0400688 cloned := proto.Clone(storeDevice).(*voltha.Device)
689 // Ensure the enums passed in are valid - they will be invalid if they are not set when this function is invoked
690 if s, ok := voltha.ConnectStatus_ConnectStatus_value[connStatus.String()]; ok {
691 log.Debugw("updateDeviceStatus-conn", log.Fields{"ok": ok, "val": s})
692 cloned.ConnectStatus = connStatus
khenaidoob9203542018-09-17 22:56:37 -0400693 }
khenaidoo92e62c52018-10-03 14:02:54 -0400694 if s, ok := voltha.OperStatus_OperStatus_value[operStatus.String()]; ok {
695 log.Debugw("updateDeviceStatus-oper", log.Fields{"ok": ok, "val": s})
696 cloned.OperStatus = operStatus
khenaidoob9203542018-09-17 22:56:37 -0400697 }
khenaidoo92e62c52018-10-03 14:02:54 -0400698 log.Debugw("updateDeviceStatus", log.Fields{"deviceId": cloned.Id, "operStatus": cloned.OperStatus, "connectStatus": cloned.ConnectStatus})
khenaidoob9203542018-09-17 22:56:37 -0400699 // Store the device
khenaidoo92e62c52018-10-03 14:02:54 -0400700 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
701 agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400702 return status.Errorf(codes.Internal, "%s", agent.deviceId)
703 }
khenaidoo92e62c52018-10-03 14:02:54 -0400704 agent.lockDevice.Unlock()
khenaidoo92e62c52018-10-03 14:02:54 -0400705 return nil
706 }
707}
708
709func (agent *DeviceAgent) updatePortState(portType voltha.Port_PortType, portNo uint32, operStatus voltha.OperStatus_OperStatus) error {
710 agent.lockDevice.Lock()
711 //defer agent.lockDevice.Unlock()
712 // Work only on latest data
713 // TODO: Get list of ports from device directly instead of the entire device
714 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
715 agent.lockDevice.Unlock()
716 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
717 } else {
718 // clone the device
719 cloned := proto.Clone(storeDevice).(*voltha.Device)
720 // Ensure the enums passed in are valid - they will be invalid if they are not set when this function is invoked
721 if _, ok := voltha.Port_PortType_value[portType.String()]; !ok {
722 agent.lockDevice.Unlock()
723 return status.Errorf(codes.InvalidArgument, "%s", portType)
724 }
725 for _, port := range cloned.Ports {
726 if port.Type == portType && port.PortNo == portNo {
727 port.OperStatus = operStatus
728 // Set the admin status to ENABLED if the operational status is ACTIVE
729 // TODO: Set by northbound system?
730 if operStatus == voltha.OperStatus_ACTIVE {
731 port.AdminState = voltha.AdminState_ENABLED
732 }
733 break
734 }
735 }
736 log.Debugw("portStatusUpdate", log.Fields{"deviceId": cloned.Id})
737 // Store the device
738 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
739 agent.lockDevice.Unlock()
740 return status.Errorf(codes.Internal, "%s", agent.deviceId)
741 }
742 agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400743 return nil
744 }
745}
746
747func (agent *DeviceAgent) updatePmConfigs(pmConfigs *voltha.PmConfigs) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400748 agent.lockDevice.Lock()
749 defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400750 log.Debug("updatePmConfigs")
751 // Work only on latest data
khenaidoo92e62c52018-10-03 14:02:54 -0400752 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400753 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
754 } else {
755 // clone the device
khenaidoo92e62c52018-10-03 14:02:54 -0400756 cloned := proto.Clone(storeDevice).(*voltha.Device)
757 cloned.PmConfigs = proto.Clone(pmConfigs).(*voltha.PmConfigs)
khenaidoob9203542018-09-17 22:56:37 -0400758 // Store the device
khenaidoo92e62c52018-10-03 14:02:54 -0400759 afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, "")
khenaidoob9203542018-09-17 22:56:37 -0400760 if afterUpdate == nil {
761 return status.Errorf(codes.Internal, "%s", agent.deviceId)
762 }
763 return nil
764 }
765}
766
767func (agent *DeviceAgent) addPort(port *voltha.Port) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400768 agent.lockDevice.Lock()
769 defer agent.lockDevice.Unlock()
770 log.Debugw("addPort", log.Fields{"deviceId": agent.deviceId})
khenaidoob9203542018-09-17 22:56:37 -0400771 // Work only on latest data
khenaidoo92e62c52018-10-03 14:02:54 -0400772 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400773 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
774 } else {
775 // clone the device
khenaidoo92e62c52018-10-03 14:02:54 -0400776 cloned := proto.Clone(storeDevice).(*voltha.Device)
khenaidoob9203542018-09-17 22:56:37 -0400777 if cloned.Ports == nil {
778 // First port
khenaidoo92e62c52018-10-03 14:02:54 -0400779 log.Debugw("addPort-first-port-to-add", log.Fields{"deviceId": agent.deviceId})
khenaidoob9203542018-09-17 22:56:37 -0400780 cloned.Ports = make([]*voltha.Port, 0)
781 }
khenaidoo92e62c52018-10-03 14:02:54 -0400782 cp := proto.Clone(port).(*voltha.Port)
783 // Set the admin state of the port to ENABLE if the operational state is ACTIVE
784 // TODO: Set by northbound system?
785 if cp.OperStatus == voltha.OperStatus_ACTIVE {
786 cp.AdminState = voltha.AdminState_ENABLED
787 }
788 cloned.Ports = append(cloned.Ports, cp)
khenaidoob9203542018-09-17 22:56:37 -0400789 // Store the device
khenaidoo92e62c52018-10-03 14:02:54 -0400790 afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, "")
791 if afterUpdate == nil {
792 return status.Errorf(codes.Internal, "%s", agent.deviceId)
793 }
794 return nil
795 }
796}
797
798func (agent *DeviceAgent) addPeerPort(port *voltha.Port_PeerPort) error {
799 agent.lockDevice.Lock()
800 defer agent.lockDevice.Unlock()
801 log.Debug("addPeerPort")
802 // Work only on latest data
803 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
804 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
805 } else {
806 // clone the device
807 cloned := proto.Clone(storeDevice).(*voltha.Device)
808 // Get the peer port on the device based on the port no
809 for _, peerPort := range cloned.Ports {
810 if peerPort.PortNo == port.PortNo { // found port
811 cp := proto.Clone(port).(*voltha.Port_PeerPort)
812 peerPort.Peers = append(peerPort.Peers, cp)
813 log.Debugw("found-peer", log.Fields{"portNo": port.PortNo, "deviceId": agent.deviceId})
814 break
815 }
816 }
817 // Store the device
818 afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, "")
khenaidoob9203542018-09-17 22:56:37 -0400819 if afterUpdate == nil {
820 return status.Errorf(codes.Internal, "%s", agent.deviceId)
821 }
822 return nil
823 }
824}
825
khenaidoo19d7b632018-10-30 10:49:50 -0400826//flowTableUpdated is the callback after flows have been updated in the model to push them
khenaidoo297cd252019-02-07 22:10:23 -0500827//to the adapters
khenaidoo19d7b632018-10-30 10:49:50 -0400828func (agent *DeviceAgent) flowTableUpdated(args ...interface{}) interface{} {
829 log.Debugw("flowTableUpdated-callback", log.Fields{"argsLen": len(args)})
830
831 agent.lockDevice.Lock()
832 defer agent.lockDevice.Unlock()
833
834 var previousData *voltha.Flows
835 var latestData *voltha.Flows
836
837 var ok bool
838 if previousData, ok = args[0].(*ofp.Flows); !ok {
839 log.Errorw("invalid-args", log.Fields{"args0": args[0]})
840 return nil
841 }
842 if latestData, ok = args[1].(*ofp.Flows); !ok {
843 log.Errorw("invalid-args", log.Fields{"args1": args[1]})
844 return nil
845 }
846
847 // Sanity check - should not happen as this is already handled in logical device agent
848 if reflect.DeepEqual(previousData.Items, latestData.Items) {
849 log.Debugw("flow-update-not-required", log.Fields{"previous": previousData.Items, "new": latestData.Items})
850 return nil
851 }
852
853 var device *voltha.Device
854 var err error
855 if device, err = agent.getDeviceWithoutLock(); err != nil {
856 log.Errorw("no-device", log.Fields{"id": agent.deviceId, "error": err})
857 return nil
858 }
859 groups := device.FlowGroups
860
khenaidoo297cd252019-02-07 22:10:23 -0500861 // Send update to adapters
khenaidoo21d51152019-02-01 13:48:37 -0500862 dType := agent.adapterMgr.getDeviceType(device.Type)
863 if !dType.AcceptsAddRemoveFlowUpdates {
khenaidoo19d7b632018-10-30 10:49:50 -0400864 if err := agent.adapterProxy.UpdateFlowsBulk(device, latestData, groups); err != nil {
865 log.Debugw("update-flow-bulk-error", log.Fields{"id": agent.lastData.Id, "error": err})
866 return err
867 }
868 return nil
869 }
870 // Incremental flow changes accepted
871 var toAdd []*ofp.OfpFlowStats
872 var toDelete []*ofp.OfpFlowStats
873
874 for _, flow := range latestData.Items {
875 if fu.FindFlowById(previousData.Items, flow) == -1 { // did not exist before
876 toAdd = append(toAdd, flow)
877 }
878 }
879 for _, flow := range previousData.Items {
880 if fu.FindFlowById(latestData.Items, flow) == -1 { // does not exist now
881 toDelete = append(toDelete, flow)
882 }
883 }
884 flowChanges := &ofp.FlowChanges{
885 ToAdd: &voltha.Flows{Items: toAdd},
886 ToRemove: &voltha.Flows{Items: toDelete},
887 }
888 // Send an empty group changes as it would be dealt with a call to groupTableUpdated
889 groupChanges := &ofp.FlowGroupChanges{}
890
891 // Send changes only
892 if err := agent.adapterProxy.UpdateFlowsIncremental(device, flowChanges, groupChanges); err != nil {
893 log.Debugw("update-flow-bulk-error", log.Fields{"id": agent.lastData.Id, "error": err})
894 return err
895 }
896
897 return nil
898}
899
900//groupTableUpdated is the callback after group table has been updated in the model to push them
khenaidoo297cd252019-02-07 22:10:23 -0500901//to the adapters
khenaidoo19d7b632018-10-30 10:49:50 -0400902func (agent *DeviceAgent) groupTableUpdated(args ...interface{}) interface{} {
903 log.Debugw("groupTableUpdated-callback", log.Fields{"argsLen": len(args)})
904
905 agent.lockDevice.Lock()
906 defer agent.lockDevice.Unlock()
907
908 var previousData *voltha.FlowGroups
909 var latestData *voltha.FlowGroups
910
911 var ok bool
912 if previousData, ok = args[0].(*ofp.FlowGroups); !ok {
913 log.Errorw("invalid-args", log.Fields{"args0": args[0]})
914 return nil
915 }
916 if latestData, ok = args[1].(*ofp.FlowGroups); !ok {
917 log.Errorw("invalid-args", log.Fields{"args1": args[1]})
918 return nil
919 }
920
921 // Sanity check - should not happen as this is already handled in logical device agent
922 if reflect.DeepEqual(previousData.Items, latestData.Items) {
923 log.Debugw("group-table-update-not-required", log.Fields{"previous": previousData.Items, "new": latestData.Items})
924 return nil
925 }
926
927 var device *voltha.Device
928 var err error
929 if device, err = agent.getDeviceWithoutLock(); err != nil {
930 log.Errorw("no-device", log.Fields{"id": agent.deviceId, "error": err})
931 return nil
932 }
933 flows := device.Flows
934
khenaidoo297cd252019-02-07 22:10:23 -0500935 // Send update to adapters
936 dType := agent.adapterMgr.getDeviceType(device.Type)
937 if !dType.AcceptsAddRemoveFlowUpdates {
khenaidoo19d7b632018-10-30 10:49:50 -0400938 if err := agent.adapterProxy.UpdateFlowsBulk(device, flows, latestData); err != nil {
939 log.Debugw("update-flows-bulk-error", log.Fields{"id": agent.lastData.Id, "error": err})
940 return err
941 }
942 return nil
943 }
944
945 // Incremental group changes accepted
946 var toAdd []*ofp.OfpGroupEntry
947 var toDelete []*ofp.OfpGroupEntry
948 var toUpdate []*ofp.OfpGroupEntry
949
950 for _, group := range latestData.Items {
951 if idx := fu.FindGroup(previousData.Items, group.Desc.GroupId); idx == -1 { // did not exist before
952 toAdd = append(toAdd, group)
953 } else { // existed before
954 if previousData.Items[idx].String() != group.String() { // there is a change
955 toUpdate = append(toUpdate, group)
956 }
957 }
958 }
959 for _, group := range previousData.Items {
960 if fu.FindGroup(latestData.Items, group.Desc.GroupId) == -1 { // does not exist now
961 toDelete = append(toDelete, group)
962 }
963 }
964 groupChanges := &ofp.FlowGroupChanges{
965 ToAdd: &voltha.FlowGroups{Items: toAdd},
966 ToRemove: &voltha.FlowGroups{Items: toDelete},
967 ToUpdate: &voltha.FlowGroups{Items: toUpdate},
968 }
969 // Send an empty flow changes as it should have been dealt with a call to flowTableUpdated
970 flowChanges := &ofp.FlowChanges{}
971
972 // Send changes only
973 if err := agent.adapterProxy.UpdateFlowsIncremental(device, flowChanges, groupChanges); err != nil {
974 log.Debugw("update-incremental-group-error", log.Fields{"id": agent.lastData.Id, "error": err})
975 return err
976 }
977 return nil
978}
979
khenaidoob9203542018-09-17 22:56:37 -0400980// TODO: A generic device update by attribute
981func (agent *DeviceAgent) updateDeviceAttribute(name string, value interface{}) {
khenaidoo92e62c52018-10-03 14:02:54 -0400982 agent.lockDevice.Lock()
983 defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400984 if value == nil {
985 return
986 }
987 var storeDevice *voltha.Device
988 var err error
khenaidoo92e62c52018-10-03 14:02:54 -0400989 if storeDevice, err = agent.getDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400990 return
991 }
992 updated := false
993 s := reflect.ValueOf(storeDevice).Elem()
994 if s.Kind() == reflect.Struct {
995 // exported field
996 f := s.FieldByName(name)
997 if f.IsValid() && f.CanSet() {
998 switch f.Kind() {
999 case reflect.String:
1000 f.SetString(value.(string))
1001 updated = true
1002 case reflect.Uint32:
1003 f.SetUint(uint64(value.(uint32)))
1004 updated = true
1005 case reflect.Bool:
1006 f.SetBool(value.(bool))
1007 updated = true
1008 }
1009 }
1010 }
khenaidoo92e62c52018-10-03 14:02:54 -04001011 log.Debugw("update-field-status", log.Fields{"deviceId": storeDevice.Id, "name": name, "updated": updated})
khenaidoob9203542018-09-17 22:56:37 -04001012 // Save the data
khenaidoo92e62c52018-10-03 14:02:54 -04001013 cloned := proto.Clone(storeDevice).(*voltha.Device)
1014 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
khenaidoob9203542018-09-17 22:56:37 -04001015 log.Warnw("attribute-update-failed", log.Fields{"attribute": name, "value": value})
1016 }
1017 return
1018}