blob: 116e2bb9b2ee84c780325625785f588974779fc6 [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"
khenaidoo79232702018-12-04 11:00:41 -050024 ic "github.com/opencord/voltha-go/protos/inter_container"
khenaidoo19d7b632018-10-30 10:49:50 -040025 ofp "github.com/opencord/voltha-go/protos/openflow_13"
khenaidoob9203542018-09-17 22:56:37 -040026 "github.com/opencord/voltha-go/protos/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)
87 }
88 } else {
89 log.Errorw("failed-to-load-device", log.Fields{"deviceId": agent.deviceId})
90 return status.Errorf(codes.NotFound, "device-%s", agent.deviceId)
91 }
92 log.Debugw("device-loaded-from-dB", log.Fields{"device": agent.lastData})
93 } else {
94 // Add the initial device to the local model
95 if added := agent.clusterDataProxy.AddWithID("/devices", agent.deviceId, agent.lastData, ""); added == nil {
96 log.Errorw("failed-to-add-device", log.Fields{"deviceId": agent.deviceId})
97 }
khenaidoob9203542018-09-17 22:56:37 -040098 }
khenaidoo297cd252019-02-07 22:10:23 -050099
khenaidoo43c82122018-11-22 18:38:28 -0500100 agent.deviceProxy = agent.clusterDataProxy.Root.CreateProxy("/devices/"+agent.deviceId, false)
101 agent.deviceProxy.RegisterCallback(model.POST_UPDATE, agent.processUpdate)
khenaidoo19d7b632018-10-30 10:49:50 -0400102
khenaidoo43c82122018-11-22 18:38:28 -0500103 agent.flowProxy = agent.clusterDataProxy.Root.CreateProxy(
khenaidoo19d7b632018-10-30 10:49:50 -0400104 fmt.Sprintf("/devices/%s/flows", agent.deviceId),
105 false)
khenaidoo43c82122018-11-22 18:38:28 -0500106 agent.groupProxy = agent.clusterDataProxy.Root.CreateProxy(
khenaidoo19d7b632018-10-30 10:49:50 -0400107 fmt.Sprintf("/devices/%s/flow_groups", agent.deviceId),
108 false)
109
110 agent.flowProxy.RegisterCallback(model.POST_UPDATE, agent.flowTableUpdated)
khenaidoo43c82122018-11-22 18:38:28 -0500111 agent.groupProxy.RegisterCallback(model.POST_UPDATE, agent.groupTableUpdated)
khenaidoo19d7b632018-10-30 10:49:50 -0400112
khenaidoob9203542018-09-17 22:56:37 -0400113 log.Debug("device-agent-started")
khenaidoo297cd252019-02-07 22:10:23 -0500114 return nil
khenaidoob9203542018-09-17 22:56:37 -0400115}
116
khenaidoo4d4802d2018-10-04 21:59:49 -0400117// stop stops the device agent. Not much to do for now
118func (agent *DeviceAgent) stop(ctx context.Context) {
khenaidoo92e62c52018-10-03 14:02:54 -0400119 agent.lockDevice.Lock()
120 defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400121 log.Debug("stopping-device-agent")
122 agent.exitChannel <- 1
123 log.Debug("device-agent-stopped")
124}
125
khenaidoo19d7b632018-10-30 10:49:50 -0400126// GetDevice retrieves the latest device information from the data model
khenaidoo92e62c52018-10-03 14:02:54 -0400127func (agent *DeviceAgent) getDevice() (*voltha.Device, error) {
128 agent.lockDevice.Lock()
129 defer agent.lockDevice.Unlock()
khenaidoo297cd252019-02-07 22:10:23 -0500130 if device := agent.clusterDataProxy.Get("/devices/"+agent.deviceId, 0, false, ""); device != nil {
khenaidoo92e62c52018-10-03 14:02:54 -0400131 if d, ok := device.(*voltha.Device); ok {
132 cloned := proto.Clone(d).(*voltha.Device)
133 return cloned, nil
134 }
135 }
136 return nil, status.Errorf(codes.NotFound, "device-%s", agent.deviceId)
137}
138
khenaidoo4d4802d2018-10-04 21:59:49 -0400139// 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 -0400140// This function is meant so that we do not have duplicate code all over the device agent functions
141func (agent *DeviceAgent) getDeviceWithoutLock() (*voltha.Device, error) {
khenaidoo297cd252019-02-07 22:10:23 -0500142 if device := agent.clusterDataProxy.Get("/devices/"+agent.deviceId, 0, false, ""); device != nil {
khenaidoo92e62c52018-10-03 14:02:54 -0400143 if d, ok := device.(*voltha.Device); ok {
144 cloned := proto.Clone(d).(*voltha.Device)
145 return cloned, nil
146 }
147 }
148 return nil, status.Errorf(codes.NotFound, "device-%s", agent.deviceId)
149}
150
khenaidoo4d4802d2018-10-04 21:59:49 -0400151// enableDevice activates a preprovisioned or disable device
khenaidoob9203542018-09-17 22:56:37 -0400152func (agent *DeviceAgent) enableDevice(ctx context.Context) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400153 agent.lockDevice.Lock()
154 defer agent.lockDevice.Unlock()
155 log.Debugw("enableDevice", log.Fields{"id": agent.deviceId})
khenaidoo21d51152019-02-01 13:48:37 -0500156
khenaidoo92e62c52018-10-03 14:02:54 -0400157 if device, err := agent.getDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400158 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
159 } else {
khenaidoo21d51152019-02-01 13:48:37 -0500160 // First figure out which adapter will handle this device type. We do it at this stage as allow devices to be
161 // pre-provisionned with the required adapter not registered. At this stage, since we need to communicate
162 // with the adapter then we need to know the adapter that will handle this request
163 if adapterName, err := agent.adapterMgr.getAdapterName(device.Type); err != nil {
164 log.Warnw("no-adapter-registered-for-device-type", log.Fields{"deviceType": device.Type, "deviceAdapter": device.Adapter})
165 return err
166 } else {
167 device.Adapter = adapterName
168 }
169
khenaidoo92e62c52018-10-03 14:02:54 -0400170 if device.AdminState == voltha.AdminState_ENABLED {
171 log.Debugw("device-already-enabled", log.Fields{"id": agent.deviceId})
172 //TODO: Needs customized error message
173 return nil
174 }
khenaidoo4d4802d2018-10-04 21:59:49 -0400175 //TODO: if parent device is disabled then do not enable device
khenaidoo92e62c52018-10-03 14:02:54 -0400176 // Verify whether we need to adopt the device the first time
177 // TODO: A state machine for these state transitions would be better (we just have to handle
178 // a limited set of states now or it may be an overkill)
179 if device.AdminState == voltha.AdminState_PREPROVISIONED {
180 // First send the request to an Adapter and wait for a response
181 if err := agent.adapterProxy.AdoptDevice(ctx, device); err != nil {
182 log.Debugw("adoptDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
khenaidoob9203542018-09-17 22:56:37 -0400183 return err
184 }
khenaidoo92e62c52018-10-03 14:02:54 -0400185 } else {
186 // First send the request to an Adapter and wait for a response
187 if err := agent.adapterProxy.ReEnableDevice(ctx, device); err != nil {
188 log.Debugw("renableDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
189 return err
190 }
191 }
192 // Received an Ack (no error found above). Now update the device in the model to the expected state
193 cloned := proto.Clone(device).(*voltha.Device)
194 cloned.AdminState = voltha.AdminState_ENABLED
195 cloned.OperStatus = voltha.OperStatus_ACTIVATING
196 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
197 return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
khenaidoob9203542018-09-17 22:56:37 -0400198 }
199 }
200 return nil
201}
202
khenaidoo19d7b632018-10-30 10:49:50 -0400203func (agent *DeviceAgent) updateFlows(flows []*ofp.OfpFlowStats) error {
204 agent.lockDevice.Lock()
205 defer agent.lockDevice.Unlock()
206 log.Debugw("updateFlows", log.Fields{"deviceId": agent.deviceId, "flows": flows})
207 var oldData *voltha.Flows
208 if storedData, err := agent.getDeviceWithoutLock(); err != nil {
209 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
210 } else {
211 oldData = proto.Clone(storedData.Flows).(*voltha.Flows)
212 log.Debugw("updateFlows", log.Fields{"deviceId": agent.deviceId, "flows": flows, "old": oldData})
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500213
khenaidoo19d7b632018-10-30 10:49:50 -0400214 // store the changed data
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500215 afterUpdate := agent.flowProxy.Update("/", &ofp.Flows{Items: flows}, false, "")
khenaidoo19d7b632018-10-30 10:49:50 -0400216 if afterUpdate == nil {
217 return status.Errorf(codes.Internal, "%s", agent.deviceId)
218 }
219
khenaidoo19d7b632018-10-30 10:49:50 -0400220 return nil
221 }
222}
223
224func (agent *DeviceAgent) updateGroups(groups []*ofp.OfpGroupEntry) error {
225 agent.lockDevice.Lock()
226 defer agent.lockDevice.Unlock()
khenaidoo19d7b632018-10-30 10:49:50 -0400227 log.Debugw("updateGroups", log.Fields{"deviceId": agent.deviceId, "groups": groups})
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500228 if _, err := agent.getDeviceWithoutLock(); err != nil {
khenaidoo19d7b632018-10-30 10:49:50 -0400229 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
230 } else {
khenaidoo19d7b632018-10-30 10:49:50 -0400231 // store the changed data
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500232 afterUpdate := agent.groupProxy.Update("/", &ofp.FlowGroups{Items: groups}, false, "")
khenaidoo19d7b632018-10-30 10:49:50 -0400233 if afterUpdate == nil {
234 return status.Errorf(codes.Internal, "%s", agent.deviceId)
235 }
236
khenaidoo19d7b632018-10-30 10:49:50 -0400237 return nil
238 }
239}
240
khenaidoo4d4802d2018-10-04 21:59:49 -0400241//disableDevice disable a device
khenaidoo92e62c52018-10-03 14:02:54 -0400242func (agent *DeviceAgent) disableDevice(ctx context.Context) error {
243 agent.lockDevice.Lock()
244 //defer agent.lockDevice.Unlock()
245 log.Debugw("disableDevice", log.Fields{"id": agent.deviceId})
246 // Get the most up to date the device info
247 if device, err := agent.getDeviceWithoutLock(); err != nil {
248 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
249 } else {
250 if device.AdminState == voltha.AdminState_DISABLED {
251 log.Debugw("device-already-disabled", log.Fields{"id": agent.deviceId})
252 //TODO: Needs customized error message
253 agent.lockDevice.Unlock()
254 return nil
255 }
256 // First send the request to an Adapter and wait for a response
257 if err := agent.adapterProxy.DisableDevice(ctx, device); err != nil {
258 log.Debugw("disableDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
259 agent.lockDevice.Unlock()
260 return err
261 }
262 // Received an Ack (no error found above). Now update the device in the model to the expected state
263 cloned := proto.Clone(device).(*voltha.Device)
264 cloned.AdminState = voltha.AdminState_DISABLED
265 // Set the state of all ports on that device to disable
266 for _, port := range cloned.Ports {
267 port.AdminState = voltha.AdminState_DISABLED
268 port.OperStatus = voltha.OperStatus_UNKNOWN
269 }
270 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
271 agent.lockDevice.Unlock()
272 return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
273 }
274 agent.lockDevice.Unlock()
khenaidoo92e62c52018-10-03 14:02:54 -0400275 }
276 return nil
277}
278
khenaidoo4d4802d2018-10-04 21:59:49 -0400279func (agent *DeviceAgent) rebootDevice(ctx context.Context) error {
280 agent.lockDevice.Lock()
281 defer agent.lockDevice.Unlock()
282 log.Debugw("rebootDevice", log.Fields{"id": agent.deviceId})
283 // Get the most up to date the device info
284 if device, err := agent.getDeviceWithoutLock(); err != nil {
285 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
286 } else {
287 if device.AdminState != voltha.AdminState_DISABLED {
288 log.Debugw("device-not-disabled", log.Fields{"id": agent.deviceId})
289 //TODO: Needs customized error message
290 return status.Errorf(codes.FailedPrecondition, "deviceId:%s, expected-admin-state:%s", agent.deviceId, voltha.AdminState_DISABLED)
291 }
292 // First send the request to an Adapter and wait for a response
293 if err := agent.adapterProxy.RebootDevice(ctx, device); err != nil {
294 log.Debugw("rebootDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
295 return err
296 }
297 }
298 return nil
299}
300
301func (agent *DeviceAgent) deleteDevice(ctx context.Context) error {
302 agent.lockDevice.Lock()
303 log.Debugw("deleteDevice", log.Fields{"id": agent.deviceId})
304 // Get the most up to date the device info
305 if device, err := agent.getDeviceWithoutLock(); err != nil {
306 agent.lockDevice.Unlock()
307 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
308 } else {
khenaidoo43c82122018-11-22 18:38:28 -0500309 if (device.AdminState != voltha.AdminState_DISABLED) &&
310 (device.AdminState != voltha.AdminState_PREPROVISIONED) {
khenaidoo4d4802d2018-10-04 21:59:49 -0400311 log.Debugw("device-not-disabled", log.Fields{"id": agent.deviceId})
312 //TODO: Needs customized error message
313 agent.lockDevice.Unlock()
314 return status.Errorf(codes.FailedPrecondition, "deviceId:%s, expected-admin-state:%s", agent.deviceId, voltha.AdminState_DISABLED)
315 }
khenaidoo7ccedd52018-12-14 16:48:54 -0500316 if device.AdminState != voltha.AdminState_PREPROVISIONED {
317 // Send the request to an Adapter and wait for a response
318 if err := agent.adapterProxy.DeleteDevice(ctx, device); err != nil {
319 log.Debugw("deleteDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
320 agent.lockDevice.Unlock()
321 return err
322 }
khenaidoo4d4802d2018-10-04 21:59:49 -0400323 }
khenaidoo7ccedd52018-12-14 16:48:54 -0500324 if removed := agent.clusterDataProxy.Remove("/devices/"+agent.deviceId, ""); removed == nil {
khenaidoo4d4802d2018-10-04 21:59:49 -0400325 agent.lockDevice.Unlock()
326 return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
327 }
328 agent.lockDevice.Unlock()
khenaidoo4d4802d2018-10-04 21:59:49 -0400329 }
330 return nil
331}
332
khenaidoof5a5bfa2019-01-23 22:20:29 -0500333func (agent *DeviceAgent) downloadImage(ctx context.Context, img *voltha.ImageDownload) (*voltha.OperationResp, error) {
334 agent.lockDevice.Lock()
335 defer agent.lockDevice.Unlock()
336 log.Debugw("downloadImage", log.Fields{"id": agent.deviceId})
337 // Get the most up to date the device info
338 if device, err := agent.getDeviceWithoutLock(); err != nil {
339 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
340 } else {
341 if device.AdminState != voltha.AdminState_ENABLED {
342 log.Debugw("device-not-enabled", log.Fields{"id": agent.deviceId})
343 return nil, status.Errorf(codes.FailedPrecondition, "deviceId:%s, expected-admin-state:%s", agent.deviceId, voltha.AdminState_ENABLED)
344 }
345 // Save the image
346 clonedImg := proto.Clone(img).(*voltha.ImageDownload)
Stephane Barbariedf5479f2019-01-29 22:13:00 -0500347 clonedImg.DownloadState = voltha.ImageDownload_DOWNLOAD_REQUESTED
khenaidoof5a5bfa2019-01-23 22:20:29 -0500348 cloned := proto.Clone(device).(*voltha.Device)
349 if cloned.ImageDownloads == nil {
350 cloned.ImageDownloads = []*voltha.ImageDownload{clonedImg}
351 } else {
352 cloned.ImageDownloads = append(cloned.ImageDownloads, clonedImg)
353 }
354 cloned.AdminState = voltha.AdminState_DOWNLOADING_IMAGE
355 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
356 return nil, status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
357 }
358 // Send the request to the adapter
359 if err := agent.adapterProxy.DownloadImage(ctx, cloned, clonedImg); err != nil {
360 log.Debugw("downloadImage-error", log.Fields{"id": agent.lastData.Id, "error": err, "image": img.Name})
361 return nil, err
362 }
363 }
364 return &voltha.OperationResp{Code: voltha.OperationResp_OPERATION_SUCCESS}, nil
365}
366
367// isImageRegistered is a helper method to figure out if an image is already registered
368func isImageRegistered(img *voltha.ImageDownload, device *voltha.Device) bool {
369 for _, image := range device.ImageDownloads {
370 if image.Id == img.Id && image.Name == img.Name {
371 return true
372 }
373 }
374 return false
375}
376
377func (agent *DeviceAgent) cancelImageDownload(ctx context.Context, img *voltha.ImageDownload) (*voltha.OperationResp, error) {
378 agent.lockDevice.Lock()
379 defer agent.lockDevice.Unlock()
380 log.Debugw("cancelImageDownload", log.Fields{"id": agent.deviceId})
381 // Get the most up to date the device info
382 if device, err := agent.getDeviceWithoutLock(); err != nil {
383 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
384 } else {
385 // Verify whether the Image is in the list of image being downloaded
386 if !isImageRegistered(img, device) {
387 return nil, status.Errorf(codes.FailedPrecondition, "deviceId:%s, image-not-registered:%s", agent.deviceId, img.Name)
388 }
389
390 // Update image download state
391 cloned := proto.Clone(device).(*voltha.Device)
392 for _, image := range cloned.ImageDownloads {
393 if image.Id == img.Id && image.Name == img.Name {
Stephane Barbariedf5479f2019-01-29 22:13:00 -0500394 image.DownloadState = voltha.ImageDownload_DOWNLOAD_CANCELLED
khenaidoof5a5bfa2019-01-23 22:20:29 -0500395 }
396 }
397
398 //If device is in downloading state, send the request to cancel the download
399 if device.AdminState == voltha.AdminState_DOWNLOADING_IMAGE {
400 if err := agent.adapterProxy.CancelImageDownload(ctx, device, img); err != nil {
401 log.Debugw("cancelImageDownload-error", log.Fields{"id": agent.lastData.Id, "error": err, "image": img.Name})
402 return nil, err
403 }
404 // Set the device to Enabled
405 cloned.AdminState = voltha.AdminState_ENABLED
406 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
407 return nil, status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
408 }
409 }
410 }
411 return &voltha.OperationResp{Code: voltha.OperationResp_OPERATION_SUCCESS}, nil
412 }
413
414func (agent *DeviceAgent) activateImage(ctx context.Context, img *voltha.ImageDownload) (*voltha.OperationResp, error) {
415 agent.lockDevice.Lock()
416 defer agent.lockDevice.Unlock()
417 log.Debugw("activateImage", log.Fields{"id": agent.deviceId})
418 // Get the most up to date the device info
419 if device, err := agent.getDeviceWithoutLock(); err != nil {
420 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
421 } else {
422 // Verify whether the Image is in the list of image being downloaded
423 if !isImageRegistered(img, device) {
424 return nil, status.Errorf(codes.FailedPrecondition, "deviceId:%s, image-not-registered:%s", agent.deviceId, img.Name)
425 }
426
427 if device.AdminState == voltha.AdminState_DOWNLOADING_IMAGE {
428 return nil, status.Errorf(codes.FailedPrecondition, "deviceId:%s, device-in-downloading-state:%s", agent.deviceId, img.Name)
429 }
430 // Update image download state
431 cloned := proto.Clone(device).(*voltha.Device)
432 for _, image := range cloned.ImageDownloads {
433 if image.Id == img.Id && image.Name == img.Name {
434 image.ImageState = voltha.ImageDownload_IMAGE_ACTIVATING
435 }
436 }
437 // Set the device to downloading_image
438 cloned.AdminState = voltha.AdminState_DOWNLOADING_IMAGE
439 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
440 return nil, status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
441 }
442
443 if err := agent.adapterProxy.ActivateImageUpdate(ctx, device, img); err != nil {
444 log.Debugw("activateImage-error", log.Fields{"id": agent.lastData.Id, "error": err, "image": img.Name})
445 return nil, err
446 }
447 // The status of the AdminState will be changed following the update_download_status response from the adapter
448 // The image name will also be removed from the device list
449 }
450 return &voltha.OperationResp{Code: voltha.OperationResp_OPERATION_SUCCESS}, nil}
451
452
453func (agent *DeviceAgent) revertImage(ctx context.Context, img *voltha.ImageDownload) (*voltha.OperationResp, error) {
454 agent.lockDevice.Lock()
455 defer agent.lockDevice.Unlock()
456 log.Debugw("revertImage", log.Fields{"id": agent.deviceId})
457 // Get the most up to date the device info
458 if device, err := agent.getDeviceWithoutLock(); err != nil {
459 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
460 } else {
461 // Verify whether the Image is in the list of image being downloaded
462 if !isImageRegistered(img, device) {
463 return nil, status.Errorf(codes.FailedPrecondition, "deviceId:%s, image-not-registered:%s", agent.deviceId, img.Name)
464 }
465
466 if device.AdminState != voltha.AdminState_ENABLED {
467 return nil, status.Errorf(codes.FailedPrecondition, "deviceId:%s, device-not-enabled-state:%s", agent.deviceId, img.Name)
468 }
469 // Update image download state
470 cloned := proto.Clone(device).(*voltha.Device)
471 for _, image := range cloned.ImageDownloads {
472 if image.Id == img.Id && image.Name == img.Name {
473 image.ImageState = voltha.ImageDownload_IMAGE_REVERTING
474 }
475 }
476 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
477 return nil, status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
478 }
479
480 if err := agent.adapterProxy.RevertImageUpdate(ctx, device, img); err != nil {
481 log.Debugw("revertImage-error", log.Fields{"id": agent.lastData.Id, "error": err, "image": img.Name})
482 return nil, err
483 }
484 }
485 return &voltha.OperationResp{Code: voltha.OperationResp_OPERATION_SUCCESS}, nil
486 }
487
488
489func (agent *DeviceAgent) getImageDownloadStatus(ctx context.Context, img *voltha.ImageDownload) (*voltha.ImageDownload, error) {
490 agent.lockDevice.Lock()
491 defer agent.lockDevice.Unlock()
492 log.Debugw("getImageDownloadStatus", log.Fields{"id": agent.deviceId})
493 // Get the most up to date the device info
494 if device, err := agent.getDeviceWithoutLock(); err != nil {
495 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
496 } else {
497 if resp, err := agent.adapterProxy.GetImageDownloadStatus(ctx, device, img); err != nil {
498 log.Debugw("getImageDownloadStatus-error", log.Fields{"id": agent.lastData.Id, "error": err, "image": img.Name})
499 return nil, err
500 } else {
501 return resp, nil
502 }
503 }
504}
505
506func (agent *DeviceAgent) updateImageDownload(img *voltha.ImageDownload) error{
507 agent.lockDevice.Lock()
508 defer agent.lockDevice.Unlock()
509 log.Debugw("updateImageDownload", log.Fields{"id": agent.deviceId})
510 // Get the most up to date the device info
511 if device, err := agent.getDeviceWithoutLock(); err != nil {
512 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
513 } else {
514 // Update the image as well as remove it if the download was cancelled
515 cloned := proto.Clone(device).(*voltha.Device)
516 clonedImages := make([]*voltha.ImageDownload, len(cloned.ImageDownloads))
517 for _, image := range cloned.ImageDownloads {
518 if image.Id == img.Id && image.Name == img.Name {
Stephane Barbariedf5479f2019-01-29 22:13:00 -0500519 if image.DownloadState != voltha.ImageDownload_DOWNLOAD_CANCELLED {
khenaidoof5a5bfa2019-01-23 22:20:29 -0500520 clonedImages = append(clonedImages, img)
521 }
522 }
523 }
524 cloned.ImageDownloads = clonedImages
525 // Set the Admin state to enabled if required
Stephane Barbariedf5479f2019-01-29 22:13:00 -0500526 if (img.DownloadState != voltha.ImageDownload_DOWNLOAD_REQUESTED &&
527 img.DownloadState != voltha.ImageDownload_DOWNLOAD_STARTED) ||
khenaidoof5a5bfa2019-01-23 22:20:29 -0500528 (img.ImageState != voltha.ImageDownload_IMAGE_ACTIVATING){
529 cloned.AdminState = voltha.AdminState_ENABLED
530 }
531
532 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
533 return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
534 }
535 }
536 return nil
537}
538
539func (agent *DeviceAgent) getImageDownload(ctx context.Context, img *voltha.ImageDownload) (*voltha.ImageDownload, error) {
540 agent.lockDevice.Lock()
541 defer agent.lockDevice.Unlock()
542 log.Debugw("getImageDownload", log.Fields{"id": agent.deviceId})
543 // Get the most up to date the device info
544 if device, err := agent.getDeviceWithoutLock(); err != nil {
545 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
546 } else {
547 for _, image := range device.ImageDownloads {
548 if image.Id == img.Id && image.Name == img.Name {
549 return image, nil
550 }
551 }
552 return nil, status.Errorf(codes.NotFound, "image-not-found:%s", img.Name)
553 }
554}
555
556func (agent *DeviceAgent) listImageDownloads(ctx context.Context, deviceId string) (*voltha.ImageDownloads, error) {
557 agent.lockDevice.Lock()
558 defer agent.lockDevice.Unlock()
559 log.Debugw("listImageDownloads", log.Fields{"id": agent.deviceId})
560 // Get the most up to date the device info
561 if device, err := agent.getDeviceWithoutLock(); err != nil {
562 return nil, status.Errorf(codes.NotFound, "%s", agent.deviceId)
563 } else {
564 return &voltha.ImageDownloads{Items:device.ImageDownloads}, nil
565 }
566}
567
khenaidoo4d4802d2018-10-04 21:59:49 -0400568// getPorts retrieves the ports information of the device based on the port type.
khenaidoo92e62c52018-10-03 14:02:54 -0400569func (agent *DeviceAgent) getPorts(ctx context.Context, portType voltha.Port_PortType) *voltha.Ports {
570 log.Debugw("getPorts", log.Fields{"id": agent.deviceId, "portType": portType})
khenaidoob9203542018-09-17 22:56:37 -0400571 ports := &voltha.Ports{}
khenaidoo19d7b632018-10-30 10:49:50 -0400572 if device, _ := agent.deviceMgr.GetDevice(agent.deviceId); device != nil {
khenaidoob9203542018-09-17 22:56:37 -0400573 for _, port := range device.Ports {
khenaidoo92e62c52018-10-03 14:02:54 -0400574 if port.Type == portType {
khenaidoob9203542018-09-17 22:56:37 -0400575 ports.Items = append(ports.Items, port)
576 }
577 }
578 }
579 return ports
580}
581
khenaidoo4d4802d2018-10-04 21:59:49 -0400582// getSwitchCapability is a helper method that a logical device agent uses to retrieve the switch capability of a
583// parent device
khenaidoo79232702018-12-04 11:00:41 -0500584func (agent *DeviceAgent) getSwitchCapability(ctx context.Context) (*ic.SwitchCapability, error) {
khenaidoob9203542018-09-17 22:56:37 -0400585 log.Debugw("getSwitchCapability", log.Fields{"deviceId": agent.deviceId})
khenaidoo19d7b632018-10-30 10:49:50 -0400586 if device, err := agent.deviceMgr.GetDevice(agent.deviceId); device == nil {
khenaidoob9203542018-09-17 22:56:37 -0400587 return nil, err
588 } else {
khenaidoo79232702018-12-04 11:00:41 -0500589 var switchCap *ic.SwitchCapability
khenaidoob9203542018-09-17 22:56:37 -0400590 var err error
591 if switchCap, err = agent.adapterProxy.GetOfpDeviceInfo(ctx, device); err != nil {
592 log.Debugw("getSwitchCapability-error", log.Fields{"id": device.Id, "error": err})
593 return nil, err
594 }
595 return switchCap, nil
596 }
597}
598
khenaidoo4d4802d2018-10-04 21:59:49 -0400599// getPortCapability is a helper method that a logical device agent uses to retrieve the port capability of a
600// device
khenaidoo79232702018-12-04 11:00:41 -0500601func (agent *DeviceAgent) getPortCapability(ctx context.Context, portNo uint32) (*ic.PortCapability, error) {
khenaidoob9203542018-09-17 22:56:37 -0400602 log.Debugw("getPortCapability", log.Fields{"deviceId": agent.deviceId})
khenaidoo19d7b632018-10-30 10:49:50 -0400603 if device, err := agent.deviceMgr.GetDevice(agent.deviceId); device == nil {
khenaidoob9203542018-09-17 22:56:37 -0400604 return nil, err
605 } else {
khenaidoo79232702018-12-04 11:00:41 -0500606 var portCap *ic.PortCapability
khenaidoob9203542018-09-17 22:56:37 -0400607 var err error
608 if portCap, err = agent.adapterProxy.GetOfpPortInfo(ctx, device, portNo); err != nil {
609 log.Debugw("getPortCapability-error", log.Fields{"id": device.Id, "error": err})
610 return nil, err
611 }
612 return portCap, nil
613 }
614}
615
khenaidoofdbad6e2018-11-06 22:26:38 -0500616func (agent *DeviceAgent) packetOut(outPort uint32, packet *ofp.OfpPacketOut) error {
617 // Send packet to adapter
618 if err := agent.adapterProxy.packetOut(agent.deviceType, agent.deviceId, outPort, packet); err != nil {
619 log.Debugw("packet-out-error", log.Fields{"id": agent.lastData.Id, "error": err})
620 return err
621 }
622 return nil
623}
624
khenaidoo4d4802d2018-10-04 21:59:49 -0400625// processUpdate is a callback invoked whenever there is a change on the device manages by this device agent
khenaidoo92e62c52018-10-03 14:02:54 -0400626func (agent *DeviceAgent) processUpdate(args ...interface{}) interface{} {
khenaidoo43c82122018-11-22 18:38:28 -0500627 //// Run this callback in its own go routine
628 go func(args ...interface{}) interface{} {
629 var previous *voltha.Device
630 var current *voltha.Device
631 var ok bool
632 if len(args) == 2 {
633 if previous, ok = args[0].(*voltha.Device); !ok {
634 log.Errorw("invalid-callback-type", log.Fields{"data": args[0]})
635 return nil
636 }
637 if current, ok = args[1].(*voltha.Device); !ok {
638 log.Errorw("invalid-callback-type", log.Fields{"data": args[1]})
639 return nil
640 }
641 } else {
642 log.Errorw("too-many-args-in-callback", log.Fields{"len": len(args)})
643 return nil
644 }
645 // Perform the state transition in it's own go routine
khenaidoof5a5bfa2019-01-23 22:20:29 -0500646 if err := agent.deviceMgr.processTransition(previous, current); err != nil {
647 log.Errorw("failed-process-transition", log.Fields{"deviceId": previous.Id,
648 "previousAdminState": previous.AdminState, "currentAdminState": current.AdminState})
649 }
khenaidoo43c82122018-11-22 18:38:28 -0500650 return nil
651 }(args...)
652
khenaidoo92e62c52018-10-03 14:02:54 -0400653 return nil
654}
655
khenaidoob9203542018-09-17 22:56:37 -0400656func (agent *DeviceAgent) updateDevice(device *voltha.Device) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400657 agent.lockDevice.Lock()
khenaidoo43c82122018-11-22 18:38:28 -0500658 defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400659 log.Debugw("updateDevice", log.Fields{"deviceId": device.Id})
khenaidoo43c82122018-11-22 18:38:28 -0500660 cloned := proto.Clone(device).(*voltha.Device)
661 afterUpdate := agent.clusterDataProxy.Update("/devices/"+device.Id, cloned, false, "")
662 if afterUpdate == nil {
663 return status.Errorf(codes.Internal, "%s", device.Id)
khenaidoob9203542018-09-17 22:56:37 -0400664 }
khenaidoo43c82122018-11-22 18:38:28 -0500665 return nil
666}
667
668func (agent *DeviceAgent) updateDeviceWithoutLock(device *voltha.Device) error {
669 log.Debugw("updateDevice", log.Fields{"deviceId": device.Id})
670 cloned := proto.Clone(device).(*voltha.Device)
671 afterUpdate := agent.clusterDataProxy.Update("/devices/"+device.Id, cloned, false, "")
672 if afterUpdate == nil {
673 return status.Errorf(codes.Internal, "%s", device.Id)
674 }
675 return nil
khenaidoob9203542018-09-17 22:56:37 -0400676}
677
khenaidoo92e62c52018-10-03 14:02:54 -0400678func (agent *DeviceAgent) updateDeviceStatus(operStatus voltha.OperStatus_OperStatus, connStatus voltha.ConnectStatus_ConnectStatus) error {
679 agent.lockDevice.Lock()
680 //defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400681 // Work only on latest data
khenaidoo92e62c52018-10-03 14:02:54 -0400682 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
683 agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400684 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
685 } else {
686 // clone the device
khenaidoo92e62c52018-10-03 14:02:54 -0400687 cloned := proto.Clone(storeDevice).(*voltha.Device)
688 // Ensure the enums passed in are valid - they will be invalid if they are not set when this function is invoked
689 if s, ok := voltha.ConnectStatus_ConnectStatus_value[connStatus.String()]; ok {
690 log.Debugw("updateDeviceStatus-conn", log.Fields{"ok": ok, "val": s})
691 cloned.ConnectStatus = connStatus
khenaidoob9203542018-09-17 22:56:37 -0400692 }
khenaidoo92e62c52018-10-03 14:02:54 -0400693 if s, ok := voltha.OperStatus_OperStatus_value[operStatus.String()]; ok {
694 log.Debugw("updateDeviceStatus-oper", log.Fields{"ok": ok, "val": s})
695 cloned.OperStatus = operStatus
khenaidoob9203542018-09-17 22:56:37 -0400696 }
khenaidoo92e62c52018-10-03 14:02:54 -0400697 log.Debugw("updateDeviceStatus", log.Fields{"deviceId": cloned.Id, "operStatus": cloned.OperStatus, "connectStatus": cloned.ConnectStatus})
khenaidoob9203542018-09-17 22:56:37 -0400698 // Store the device
khenaidoo92e62c52018-10-03 14:02:54 -0400699 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
700 agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400701 return status.Errorf(codes.Internal, "%s", agent.deviceId)
702 }
khenaidoo92e62c52018-10-03 14:02:54 -0400703 agent.lockDevice.Unlock()
khenaidoo92e62c52018-10-03 14:02:54 -0400704 return nil
705 }
706}
707
708func (agent *DeviceAgent) updatePortState(portType voltha.Port_PortType, portNo uint32, operStatus voltha.OperStatus_OperStatus) error {
709 agent.lockDevice.Lock()
710 //defer agent.lockDevice.Unlock()
711 // Work only on latest data
712 // TODO: Get list of ports from device directly instead of the entire device
713 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
714 agent.lockDevice.Unlock()
715 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
716 } else {
717 // clone the device
718 cloned := proto.Clone(storeDevice).(*voltha.Device)
719 // Ensure the enums passed in are valid - they will be invalid if they are not set when this function is invoked
720 if _, ok := voltha.Port_PortType_value[portType.String()]; !ok {
721 agent.lockDevice.Unlock()
722 return status.Errorf(codes.InvalidArgument, "%s", portType)
723 }
724 for _, port := range cloned.Ports {
725 if port.Type == portType && port.PortNo == portNo {
726 port.OperStatus = operStatus
727 // Set the admin status to ENABLED if the operational status is ACTIVE
728 // TODO: Set by northbound system?
729 if operStatus == voltha.OperStatus_ACTIVE {
730 port.AdminState = voltha.AdminState_ENABLED
731 }
732 break
733 }
734 }
735 log.Debugw("portStatusUpdate", log.Fields{"deviceId": cloned.Id})
736 // Store the device
737 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
738 agent.lockDevice.Unlock()
739 return status.Errorf(codes.Internal, "%s", agent.deviceId)
740 }
741 agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400742 return nil
743 }
744}
745
746func (agent *DeviceAgent) updatePmConfigs(pmConfigs *voltha.PmConfigs) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400747 agent.lockDevice.Lock()
748 defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400749 log.Debug("updatePmConfigs")
750 // Work only on latest data
khenaidoo92e62c52018-10-03 14:02:54 -0400751 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400752 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
753 } else {
754 // clone the device
khenaidoo92e62c52018-10-03 14:02:54 -0400755 cloned := proto.Clone(storeDevice).(*voltha.Device)
756 cloned.PmConfigs = proto.Clone(pmConfigs).(*voltha.PmConfigs)
khenaidoob9203542018-09-17 22:56:37 -0400757 // Store the device
khenaidoo92e62c52018-10-03 14:02:54 -0400758 afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, "")
khenaidoob9203542018-09-17 22:56:37 -0400759 if afterUpdate == nil {
760 return status.Errorf(codes.Internal, "%s", agent.deviceId)
761 }
762 return nil
763 }
764}
765
766func (agent *DeviceAgent) addPort(port *voltha.Port) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400767 agent.lockDevice.Lock()
768 defer agent.lockDevice.Unlock()
769 log.Debugw("addPort", log.Fields{"deviceId": agent.deviceId})
khenaidoob9203542018-09-17 22:56:37 -0400770 // Work only on latest data
khenaidoo92e62c52018-10-03 14:02:54 -0400771 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400772 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
773 } else {
774 // clone the device
khenaidoo92e62c52018-10-03 14:02:54 -0400775 cloned := proto.Clone(storeDevice).(*voltha.Device)
khenaidoob9203542018-09-17 22:56:37 -0400776 if cloned.Ports == nil {
777 // First port
khenaidoo92e62c52018-10-03 14:02:54 -0400778 log.Debugw("addPort-first-port-to-add", log.Fields{"deviceId": agent.deviceId})
khenaidoob9203542018-09-17 22:56:37 -0400779 cloned.Ports = make([]*voltha.Port, 0)
780 }
khenaidoo92e62c52018-10-03 14:02:54 -0400781 cp := proto.Clone(port).(*voltha.Port)
782 // Set the admin state of the port to ENABLE if the operational state is ACTIVE
783 // TODO: Set by northbound system?
784 if cp.OperStatus == voltha.OperStatus_ACTIVE {
785 cp.AdminState = voltha.AdminState_ENABLED
786 }
787 cloned.Ports = append(cloned.Ports, cp)
khenaidoob9203542018-09-17 22:56:37 -0400788 // Store the device
khenaidoo92e62c52018-10-03 14:02:54 -0400789 afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, "")
790 if afterUpdate == nil {
791 return status.Errorf(codes.Internal, "%s", agent.deviceId)
792 }
793 return nil
794 }
795}
796
797func (agent *DeviceAgent) addPeerPort(port *voltha.Port_PeerPort) error {
798 agent.lockDevice.Lock()
799 defer agent.lockDevice.Unlock()
800 log.Debug("addPeerPort")
801 // Work only on latest data
802 if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
803 return status.Errorf(codes.NotFound, "%s", agent.deviceId)
804 } else {
805 // clone the device
806 cloned := proto.Clone(storeDevice).(*voltha.Device)
807 // Get the peer port on the device based on the port no
808 for _, peerPort := range cloned.Ports {
809 if peerPort.PortNo == port.PortNo { // found port
810 cp := proto.Clone(port).(*voltha.Port_PeerPort)
811 peerPort.Peers = append(peerPort.Peers, cp)
812 log.Debugw("found-peer", log.Fields{"portNo": port.PortNo, "deviceId": agent.deviceId})
813 break
814 }
815 }
816 // Store the device
817 afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, "")
khenaidoob9203542018-09-17 22:56:37 -0400818 if afterUpdate == nil {
819 return status.Errorf(codes.Internal, "%s", agent.deviceId)
820 }
821 return nil
822 }
823}
824
khenaidoo19d7b632018-10-30 10:49:50 -0400825//flowTableUpdated is the callback after flows have been updated in the model to push them
khenaidoo297cd252019-02-07 22:10:23 -0500826//to the adapters
khenaidoo19d7b632018-10-30 10:49:50 -0400827func (agent *DeviceAgent) flowTableUpdated(args ...interface{}) interface{} {
828 log.Debugw("flowTableUpdated-callback", log.Fields{"argsLen": len(args)})
829
830 agent.lockDevice.Lock()
831 defer agent.lockDevice.Unlock()
832
833 var previousData *voltha.Flows
834 var latestData *voltha.Flows
835
836 var ok bool
837 if previousData, ok = args[0].(*ofp.Flows); !ok {
838 log.Errorw("invalid-args", log.Fields{"args0": args[0]})
839 return nil
840 }
841 if latestData, ok = args[1].(*ofp.Flows); !ok {
842 log.Errorw("invalid-args", log.Fields{"args1": args[1]})
843 return nil
844 }
845
846 // Sanity check - should not happen as this is already handled in logical device agent
847 if reflect.DeepEqual(previousData.Items, latestData.Items) {
848 log.Debugw("flow-update-not-required", log.Fields{"previous": previousData.Items, "new": latestData.Items})
849 return nil
850 }
851
852 var device *voltha.Device
853 var err error
854 if device, err = agent.getDeviceWithoutLock(); err != nil {
855 log.Errorw("no-device", log.Fields{"id": agent.deviceId, "error": err})
856 return nil
857 }
858 groups := device.FlowGroups
859
khenaidoo297cd252019-02-07 22:10:23 -0500860 // Send update to adapters
khenaidoo21d51152019-02-01 13:48:37 -0500861 dType := agent.adapterMgr.getDeviceType(device.Type)
862 if !dType.AcceptsAddRemoveFlowUpdates {
khenaidoo19d7b632018-10-30 10:49:50 -0400863 if err := agent.adapterProxy.UpdateFlowsBulk(device, latestData, groups); err != nil {
864 log.Debugw("update-flow-bulk-error", log.Fields{"id": agent.lastData.Id, "error": err})
865 return err
866 }
867 return nil
868 }
869 // Incremental flow changes accepted
870 var toAdd []*ofp.OfpFlowStats
871 var toDelete []*ofp.OfpFlowStats
872
873 for _, flow := range latestData.Items {
874 if fu.FindFlowById(previousData.Items, flow) == -1 { // did not exist before
875 toAdd = append(toAdd, flow)
876 }
877 }
878 for _, flow := range previousData.Items {
879 if fu.FindFlowById(latestData.Items, flow) == -1 { // does not exist now
880 toDelete = append(toDelete, flow)
881 }
882 }
883 flowChanges := &ofp.FlowChanges{
884 ToAdd: &voltha.Flows{Items: toAdd},
885 ToRemove: &voltha.Flows{Items: toDelete},
886 }
887 // Send an empty group changes as it would be dealt with a call to groupTableUpdated
888 groupChanges := &ofp.FlowGroupChanges{}
889
890 // Send changes only
891 if err := agent.adapterProxy.UpdateFlowsIncremental(device, flowChanges, groupChanges); err != nil {
892 log.Debugw("update-flow-bulk-error", log.Fields{"id": agent.lastData.Id, "error": err})
893 return err
894 }
895
896 return nil
897}
898
899//groupTableUpdated is the callback after group table has been updated in the model to push them
khenaidoo297cd252019-02-07 22:10:23 -0500900//to the adapters
khenaidoo19d7b632018-10-30 10:49:50 -0400901func (agent *DeviceAgent) groupTableUpdated(args ...interface{}) interface{} {
902 log.Debugw("groupTableUpdated-callback", log.Fields{"argsLen": len(args)})
903
904 agent.lockDevice.Lock()
905 defer agent.lockDevice.Unlock()
906
907 var previousData *voltha.FlowGroups
908 var latestData *voltha.FlowGroups
909
910 var ok bool
911 if previousData, ok = args[0].(*ofp.FlowGroups); !ok {
912 log.Errorw("invalid-args", log.Fields{"args0": args[0]})
913 return nil
914 }
915 if latestData, ok = args[1].(*ofp.FlowGroups); !ok {
916 log.Errorw("invalid-args", log.Fields{"args1": args[1]})
917 return nil
918 }
919
920 // Sanity check - should not happen as this is already handled in logical device agent
921 if reflect.DeepEqual(previousData.Items, latestData.Items) {
922 log.Debugw("group-table-update-not-required", log.Fields{"previous": previousData.Items, "new": latestData.Items})
923 return nil
924 }
925
926 var device *voltha.Device
927 var err error
928 if device, err = agent.getDeviceWithoutLock(); err != nil {
929 log.Errorw("no-device", log.Fields{"id": agent.deviceId, "error": err})
930 return nil
931 }
932 flows := device.Flows
933
khenaidoo297cd252019-02-07 22:10:23 -0500934 // Send update to adapters
935 dType := agent.adapterMgr.getDeviceType(device.Type)
936 if !dType.AcceptsAddRemoveFlowUpdates {
khenaidoo19d7b632018-10-30 10:49:50 -0400937 if err := agent.adapterProxy.UpdateFlowsBulk(device, flows, latestData); err != nil {
938 log.Debugw("update-flows-bulk-error", log.Fields{"id": agent.lastData.Id, "error": err})
939 return err
940 }
941 return nil
942 }
943
944 // Incremental group changes accepted
945 var toAdd []*ofp.OfpGroupEntry
946 var toDelete []*ofp.OfpGroupEntry
947 var toUpdate []*ofp.OfpGroupEntry
948
949 for _, group := range latestData.Items {
950 if idx := fu.FindGroup(previousData.Items, group.Desc.GroupId); idx == -1 { // did not exist before
951 toAdd = append(toAdd, group)
952 } else { // existed before
953 if previousData.Items[idx].String() != group.String() { // there is a change
954 toUpdate = append(toUpdate, group)
955 }
956 }
957 }
958 for _, group := range previousData.Items {
959 if fu.FindGroup(latestData.Items, group.Desc.GroupId) == -1 { // does not exist now
960 toDelete = append(toDelete, group)
961 }
962 }
963 groupChanges := &ofp.FlowGroupChanges{
964 ToAdd: &voltha.FlowGroups{Items: toAdd},
965 ToRemove: &voltha.FlowGroups{Items: toDelete},
966 ToUpdate: &voltha.FlowGroups{Items: toUpdate},
967 }
968 // Send an empty flow changes as it should have been dealt with a call to flowTableUpdated
969 flowChanges := &ofp.FlowChanges{}
970
971 // Send changes only
972 if err := agent.adapterProxy.UpdateFlowsIncremental(device, flowChanges, groupChanges); err != nil {
973 log.Debugw("update-incremental-group-error", log.Fields{"id": agent.lastData.Id, "error": err})
974 return err
975 }
976 return nil
977}
978
khenaidoob9203542018-09-17 22:56:37 -0400979// TODO: A generic device update by attribute
980func (agent *DeviceAgent) updateDeviceAttribute(name string, value interface{}) {
khenaidoo92e62c52018-10-03 14:02:54 -0400981 agent.lockDevice.Lock()
982 defer agent.lockDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400983 if value == nil {
984 return
985 }
986 var storeDevice *voltha.Device
987 var err error
khenaidoo92e62c52018-10-03 14:02:54 -0400988 if storeDevice, err = agent.getDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400989 return
990 }
991 updated := false
992 s := reflect.ValueOf(storeDevice).Elem()
993 if s.Kind() == reflect.Struct {
994 // exported field
995 f := s.FieldByName(name)
996 if f.IsValid() && f.CanSet() {
997 switch f.Kind() {
998 case reflect.String:
999 f.SetString(value.(string))
1000 updated = true
1001 case reflect.Uint32:
1002 f.SetUint(uint64(value.(uint32)))
1003 updated = true
1004 case reflect.Bool:
1005 f.SetBool(value.(bool))
1006 updated = true
1007 }
1008 }
1009 }
khenaidoo92e62c52018-10-03 14:02:54 -04001010 log.Debugw("update-field-status", log.Fields{"deviceId": storeDevice.Id, "name": name, "updated": updated})
khenaidoob9203542018-09-17 22:56:37 -04001011 // Save the data
khenaidoo92e62c52018-10-03 14:02:54 -04001012 cloned := proto.Clone(storeDevice).(*voltha.Device)
1013 if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
khenaidoob9203542018-09-17 22:56:37 -04001014 log.Warnw("attribute-update-failed", log.Fields{"attribute": name, "value": value})
1015 }
1016 return
1017}