blob: faf8da808aa3d0994c725b37fb704a9cd12f478f [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"
20 "errors"
21 "github.com/opencord/voltha-go/common/log"
22 "github.com/opencord/voltha-go/db/model"
23 "github.com/opencord/voltha-go/kafka"
khenaidoo3d3b8c22019-05-22 18:10:39 -040024 "github.com/opencord/voltha-go/rw_core/utils"
William Kurkiandaa6bb22019-03-07 12:26:28 -050025 ic "github.com/opencord/voltha-protos/go/inter_container"
26 ofp "github.com/opencord/voltha-protos/go/openflow_13"
27 "github.com/opencord/voltha-protos/go/voltha"
khenaidoob9203542018-09-17 22:56:37 -040028 "google.golang.org/grpc/codes"
29 "google.golang.org/grpc/status"
30 "reflect"
31 "runtime"
32 "sync"
33)
34
35type DeviceManager struct {
36 deviceAgents map[string]*DeviceAgent
khenaidoo2c6a0992019-04-29 13:46:56 -040037 rootDevices map[string]bool
38 lockRootDeviceMap sync.RWMutex
Richard Jankowski199fd862019-03-18 14:49:51 -040039 core *Core
khenaidoob9203542018-09-17 22:56:37 -040040 adapterProxy *AdapterProxy
khenaidoo297cd252019-02-07 22:10:23 -050041 adapterMgr *AdapterManager
khenaidoob9203542018-09-17 22:56:37 -040042 logicalDeviceMgr *LogicalDeviceManager
khenaidoo43c82122018-11-22 18:38:28 -050043 kafkaICProxy *kafka.InterContainerProxy
khenaidoob9203542018-09-17 22:56:37 -040044 stateTransitions *TransitionMap
khenaidoo9a468962018-09-19 15:33:13 -040045 clusterDataProxy *model.Proxy
khenaidood2b6df92018-12-13 16:37:20 -050046 coreInstanceId string
khenaidoob9203542018-09-17 22:56:37 -040047 exitChannel chan int
khenaidoo2c6a0992019-04-29 13:46:56 -040048 defaultTimeout int64
khenaidoob9203542018-09-17 22:56:37 -040049 lockDeviceAgentsMap sync.RWMutex
50}
51
Richard Jankowski199fd862019-03-18 14:49:51 -040052func newDeviceManager(core *Core) *DeviceManager {
khenaidoob9203542018-09-17 22:56:37 -040053 var deviceMgr DeviceManager
Richard Jankowski199fd862019-03-18 14:49:51 -040054 deviceMgr.core = core
khenaidoob9203542018-09-17 22:56:37 -040055 deviceMgr.exitChannel = make(chan int, 1)
56 deviceMgr.deviceAgents = make(map[string]*DeviceAgent)
khenaidoo2c6a0992019-04-29 13:46:56 -040057 deviceMgr.rootDevices = make(map[string]bool)
Richard Jankowski199fd862019-03-18 14:49:51 -040058 deviceMgr.kafkaICProxy = core.kmp
59 deviceMgr.adapterProxy = NewAdapterProxy(core.kmp)
60 deviceMgr.coreInstanceId = core.instanceId
61 deviceMgr.clusterDataProxy = core.clusterDataProxy
62 deviceMgr.adapterMgr = core.adapterMgr
khenaidoob9203542018-09-17 22:56:37 -040063 deviceMgr.lockDeviceAgentsMap = sync.RWMutex{}
khenaidoo2c6a0992019-04-29 13:46:56 -040064 deviceMgr.lockRootDeviceMap = sync.RWMutex{}
65 deviceMgr.defaultTimeout = core.config.DefaultCoreTimeout
khenaidoob9203542018-09-17 22:56:37 -040066 return &deviceMgr
67}
68
khenaidoo4d4802d2018-10-04 21:59:49 -040069func (dMgr *DeviceManager) start(ctx context.Context, logicalDeviceMgr *LogicalDeviceManager) {
khenaidoob9203542018-09-17 22:56:37 -040070 log.Info("starting-device-manager")
71 dMgr.logicalDeviceMgr = logicalDeviceMgr
72 dMgr.stateTransitions = NewTransitionMap(dMgr)
73 log.Info("device-manager-started")
74}
75
khenaidoo4d4802d2018-10-04 21:59:49 -040076func (dMgr *DeviceManager) stop(ctx context.Context) {
khenaidoob9203542018-09-17 22:56:37 -040077 log.Info("stopping-device-manager")
78 dMgr.exitChannel <- 1
79 log.Info("device-manager-stopped")
80}
81
82func sendResponse(ctx context.Context, ch chan interface{}, result interface{}) {
83 if ctx.Err() == nil {
84 // Returned response only of the ctx has not been cancelled/timeout/etc
85 // Channel is automatically closed when a context is Done
86 ch <- result
87 log.Debugw("sendResponse", log.Fields{"result": result})
88 } else {
89 // Should the transaction be reverted back?
90 log.Debugw("sendResponse-context-error", log.Fields{"context-error": ctx.Err()})
91 }
92}
93
94func (dMgr *DeviceManager) addDeviceAgentToMap(agent *DeviceAgent) {
95 dMgr.lockDeviceAgentsMap.Lock()
khenaidoo2c6a0992019-04-29 13:46:56 -040096 //defer dMgr.lockDeviceAgentsMap.Unlock()
khenaidoob9203542018-09-17 22:56:37 -040097 if _, exist := dMgr.deviceAgents[agent.deviceId]; !exist {
98 dMgr.deviceAgents[agent.deviceId] = agent
99 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400100 dMgr.lockDeviceAgentsMap.Unlock()
101 dMgr.lockRootDeviceMap.Lock()
102 defer dMgr.lockRootDeviceMap.Unlock()
103 dMgr.rootDevices[agent.deviceId] = agent.isRootdevice
104
khenaidoob9203542018-09-17 22:56:37 -0400105}
106
khenaidoo4d4802d2018-10-04 21:59:49 -0400107func (dMgr *DeviceManager) deleteDeviceAgentToMap(agent *DeviceAgent) {
108 dMgr.lockDeviceAgentsMap.Lock()
khenaidoo2c6a0992019-04-29 13:46:56 -0400109 //defer dMgr.lockDeviceAgentsMap.Unlock()
khenaidoo4d4802d2018-10-04 21:59:49 -0400110 delete(dMgr.deviceAgents, agent.deviceId)
khenaidoo2c6a0992019-04-29 13:46:56 -0400111 dMgr.lockDeviceAgentsMap.Unlock()
112 dMgr.lockRootDeviceMap.Lock()
113 defer dMgr.lockRootDeviceMap.Unlock()
114 delete(dMgr.rootDevices, agent.deviceId)
115
khenaidoo4d4802d2018-10-04 21:59:49 -0400116}
117
khenaidoo297cd252019-02-07 22:10:23 -0500118// getDeviceAgent returns the agent managing the device. If the device is not in memory, it will loads it, if it exists
khenaidoob9203542018-09-17 22:56:37 -0400119func (dMgr *DeviceManager) getDeviceAgent(deviceId string) *DeviceAgent {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400120 dMgr.lockDeviceAgentsMap.RLock()
khenaidoob9203542018-09-17 22:56:37 -0400121 if agent, ok := dMgr.deviceAgents[deviceId]; ok {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400122 dMgr.lockDeviceAgentsMap.RUnlock()
khenaidoob9203542018-09-17 22:56:37 -0400123 return agent
khenaidoo297cd252019-02-07 22:10:23 -0500124 } else {
khenaidoo3d3b8c22019-05-22 18:10:39 -0400125 // Try to load into memory - loading will also create the device agent and set the device ownership
khenaidoo1ce37ad2019-03-24 22:07:24 -0400126 dMgr.lockDeviceAgentsMap.RUnlock()
khenaidoo297cd252019-02-07 22:10:23 -0500127 if err := dMgr.load(deviceId); err == nil {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400128 dMgr.lockDeviceAgentsMap.RLock()
129 defer dMgr.lockDeviceAgentsMap.RUnlock()
khenaidoo3d3b8c22019-05-22 18:10:39 -0400130 if agent, ok = dMgr.deviceAgents[deviceId]; !ok {
131 return nil
132 } else {
133 // Register this device for ownership tracking
134 go dMgr.core.deviceOwnership.OwnedByMe(&utils.DeviceID{Id: deviceId})
khenaidoo297cd252019-02-07 22:10:23 -0500135 return agent
136 }
khenaidoo3d3b8c22019-05-22 18:10:39 -0400137 } else {
138 //TODO: Change the return params to return an error as well
139 log.Errorw("loading-device-failed", log.Fields{"deviceId": deviceId, "error": err})
khenaidoo297cd252019-02-07 22:10:23 -0500140 }
khenaidoob9203542018-09-17 22:56:37 -0400141 }
142 return nil
143}
144
khenaidoo297cd252019-02-07 22:10:23 -0500145// listDeviceIdsFromMap returns the list of device IDs that are in memory
khenaidoo7ccedd52018-12-14 16:48:54 -0500146func (dMgr *DeviceManager) listDeviceIdsFromMap() *voltha.IDs {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400147 dMgr.lockDeviceAgentsMap.RLock()
148 defer dMgr.lockDeviceAgentsMap.RUnlock()
khenaidoo7ccedd52018-12-14 16:48:54 -0500149 result := &voltha.IDs{Items: make([]*voltha.ID, 0)}
khenaidoo2c6a0992019-04-29 13:46:56 -0400150 for key := range dMgr.deviceAgents {
khenaidoo7ccedd52018-12-14 16:48:54 -0500151 result.Items = append(result.Items, &voltha.ID{Id: key})
152 }
153 return result
154}
155
khenaidoob9203542018-09-17 22:56:37 -0400156func (dMgr *DeviceManager) createDevice(ctx context.Context, device *voltha.Device, ch chan interface{}) {
khenaidoo92e62c52018-10-03 14:02:54 -0400157 log.Debugw("createDevice", log.Fields{"device": device, "aproxy": dMgr.adapterProxy})
khenaidoob9203542018-09-17 22:56:37 -0400158
khenaidoo5e677ae2019-02-28 17:26:29 -0500159 // Ensure this device is set as root
160 device.Root = true
khenaidoob9203542018-09-17 22:56:37 -0400161 // Create and start a device agent for that device
khenaidoo2c6a0992019-04-29 13:46:56 -0400162 agent := newDeviceAgent(dMgr.adapterProxy, device, dMgr, dMgr.clusterDataProxy, dMgr.defaultTimeout)
khenaidoob9203542018-09-17 22:56:37 -0400163 dMgr.addDeviceAgentToMap(agent)
khenaidoo297cd252019-02-07 22:10:23 -0500164 agent.start(ctx, false)
khenaidoob9203542018-09-17 22:56:37 -0400165
khenaidoo92e62c52018-10-03 14:02:54 -0400166 sendResponse(ctx, ch, agent.lastData)
khenaidoob9203542018-09-17 22:56:37 -0400167}
168
169func (dMgr *DeviceManager) enableDevice(ctx context.Context, id *voltha.ID, ch chan interface{}) {
khenaidoo92e62c52018-10-03 14:02:54 -0400170 log.Debugw("enableDevice", log.Fields{"deviceid": id})
khenaidoob9203542018-09-17 22:56:37 -0400171 var res interface{}
172 if agent := dMgr.getDeviceAgent(id.Id); agent != nil {
173 res = agent.enableDevice(ctx)
174 log.Debugw("EnableDevice-result", log.Fields{"result": res})
khenaidoob9203542018-09-17 22:56:37 -0400175 }
176
177 sendResponse(ctx, ch, res)
178}
179
khenaidoo92e62c52018-10-03 14:02:54 -0400180func (dMgr *DeviceManager) disableDevice(ctx context.Context, id *voltha.ID, ch chan interface{}) {
181 log.Debugw("disableDevice", log.Fields{"deviceid": id})
khenaidoo92e62c52018-10-03 14:02:54 -0400182 var res interface{}
183 if agent := dMgr.getDeviceAgent(id.Id); agent != nil {
184 res = agent.disableDevice(ctx)
185 log.Debugw("disableDevice-result", log.Fields{"result": res})
khenaidoob9203542018-09-17 22:56:37 -0400186 } else {
khenaidoo92e62c52018-10-03 14:02:54 -0400187 res = status.Errorf(codes.NotFound, "%s", id.Id)
khenaidoob9203542018-09-17 22:56:37 -0400188 }
khenaidoo92e62c52018-10-03 14:02:54 -0400189
190 sendResponse(ctx, ch, res)
191}
192
khenaidoo4d4802d2018-10-04 21:59:49 -0400193func (dMgr *DeviceManager) rebootDevice(ctx context.Context, id *voltha.ID, ch chan interface{}) {
194 log.Debugw("rebootDevice", log.Fields{"deviceid": id})
195 var res interface{}
196 if agent := dMgr.getDeviceAgent(id.Id); agent != nil {
197 res = agent.rebootDevice(ctx)
198 log.Debugw("rebootDevice-result", log.Fields{"result": res})
199 } else {
200 res = status.Errorf(codes.NotFound, "%s", id.Id)
201 }
202 sendResponse(ctx, ch, res)
203}
204
205func (dMgr *DeviceManager) deleteDevice(ctx context.Context, id *voltha.ID, ch chan interface{}) {
206 log.Debugw("deleteDevice", log.Fields{"deviceid": id})
207 var res interface{}
208 if agent := dMgr.getDeviceAgent(id.Id); agent != nil {
209 res = agent.deleteDevice(ctx)
khenaidoo4d4802d2018-10-04 21:59:49 -0400210 log.Debugw("deleteDevice-result", log.Fields{"result": res})
211 } else {
212 res = status.Errorf(codes.NotFound, "%s", id.Id)
213 }
214 sendResponse(ctx, ch, res)
215}
216
khenaidoo6d62c002019-05-15 21:57:03 -0400217// stopManagingDevice stops the management of the device as well as any of its reference device and logical device.
218// This function is called only in the Core that does not own this device. In the Core that owns this device then a
219// deletion deletion also includes removal of any reference of this device.
220func (dMgr *DeviceManager) stopManagingDevice(id string) {
221 log.Infow("stopManagingDevice", log.Fields{"deviceId": id})
222 if dMgr.IsDeviceInCache(id) { // Proceed only if an agent is present for this device
223 if root, _ := dMgr.IsRootDevice(id); root == true {
224 // stop managing the logical device
225 ldeviceId := dMgr.logicalDeviceMgr.stopManagingLogicalDeviceWithDeviceId(id)
226 if ldeviceId != "" { // Can happen if logical device agent was already stopped
227 dMgr.core.deviceOwnership.AbandonDevice(ldeviceId)
228 }
229 // stop managing the child devices
230 childDeviceIds := dMgr.getAllDeviceIdsWithDeviceParentId(id)
231 for _, cId := range childDeviceIds {
232 dMgr.stopManagingDevice(cId)
233 }
234 }
235 if agent := dMgr.getDeviceAgent(id); agent != nil {
236 agent.stop(nil)
237 dMgr.deleteDeviceAgentToMap(agent)
238 // Abandon the device ownership
239 dMgr.core.deviceOwnership.AbandonDevice(id)
240 }
241 }
242}
243
khenaidoo0a822f92019-05-08 15:15:57 -0400244func (dMgr *DeviceManager) RunPostDeviceDelete(cDevice *voltha.Device) error {
245 log.Infow("RunPostDeviceDelete", log.Fields{"deviceId": cDevice.Id})
khenaidoo6d62c002019-05-15 21:57:03 -0400246 dMgr.stopManagingDevice(cDevice.Id)
247 return nil
khenaidoo0a822f92019-05-08 15:15:57 -0400248}
249
khenaidoo297cd252019-02-07 22:10:23 -0500250// GetDevice will returns a device, either from memory or from the dB, if present
khenaidoo19d7b632018-10-30 10:49:50 -0400251func (dMgr *DeviceManager) GetDevice(id string) (*voltha.Device, error) {
252 log.Debugw("GetDevice", log.Fields{"deviceid": id})
khenaidoo92e62c52018-10-03 14:02:54 -0400253 if agent := dMgr.getDeviceAgent(id); agent != nil {
254 return agent.getDevice()
255 }
256 return nil, status.Errorf(codes.NotFound, "%s", id)
khenaidoob9203542018-09-17 22:56:37 -0400257}
258
Matt Jeanneret4e241952019-02-28 11:16:04 -0500259func (dMgr *DeviceManager) GetChildDevice(parentDeviceId string, serialNumber string, onuId int64, parentPortNo int64) (*voltha.Device, error) {
Matt Jeanneret0c5088c2019-04-22 16:16:19 -0400260 log.Debugw("GetChildDevice", log.Fields{"parentDeviceid": parentDeviceId, "serialNumber": serialNumber,
261 "parentPortNo": parentPortNo, "onuId": onuId})
Matt Jeanneret4e241952019-02-28 11:16:04 -0500262
263 var parentDevice *voltha.Device
264 var err error
265 if parentDevice, err = dMgr.GetDevice(parentDeviceId); err != nil {
266 return nil, status.Errorf(codes.Aborted, "%s", err.Error())
267 }
268 var childDeviceIds []string
269 if childDeviceIds, err = dMgr.getAllChildDeviceIds(parentDevice); err != nil {
270 return nil, status.Errorf(codes.Aborted, "%s", err.Error())
271 }
272 if len(childDeviceIds) == 0 {
273 log.Debugw("no-child-devices", log.Fields{"parentDeviceId": parentDevice.Id})
274 return nil, status.Errorf(codes.NotFound, "%s", parentDeviceId)
275 }
276
277 var foundChildDevice *voltha.Device
278 for _, childDeviceId := range childDeviceIds {
279 found := false
280 if searchDevice, err := dMgr.GetDevice(childDeviceId); err == nil {
281
282 foundOnuId := false
283 if searchDevice.ProxyAddress.OnuId == uint32(onuId) {
284 if searchDevice.ParentPortNo == uint32(parentPortNo) {
285 log.Debugw("found-child-by-onuid", log.Fields{"parentDeviceId": parentDevice.Id, "onuId": onuId})
286 foundOnuId = true
287 }
288 }
289
290 foundSerialNumber := false
291 if searchDevice.SerialNumber == serialNumber {
292 log.Debugw("found-child-by-serialnumber", log.Fields{"parentDeviceId": parentDevice.Id, "serialNumber": serialNumber})
293 foundSerialNumber = true
294 }
295
296 // if both onuId and serialNumber are provided both must be true for the device to be found
297 // otherwise whichever one found a match is good enough
298 if onuId > 0 && serialNumber != "" {
299 found = foundOnuId && foundSerialNumber
300 } else {
301 found = foundOnuId || foundSerialNumber
302 }
303
304 if found == true {
305 foundChildDevice = searchDevice
306 break
307 }
308 }
309 }
310
311 if foundChildDevice != nil {
312 log.Debugw("child-device-found", log.Fields{"parentDeviceId": parentDevice.Id, "foundChildDevice": foundChildDevice})
313 return foundChildDevice, nil
314 }
315
316 log.Warnw("child-device-not-found", log.Fields{"parentDeviceId": parentDevice.Id,
317 "serialNumber": serialNumber, "onuId": onuId, "parentPortNo": parentPortNo})
318 return nil, status.Errorf(codes.NotFound, "%s", parentDeviceId)
319}
320
Matt Jeanneret2a20aaa2019-03-05 21:04:02 -0500321func (dMgr *DeviceManager) GetChildDeviceWithProxyAddress(proxyAddress *voltha.Device_ProxyAddress) (*voltha.Device, error) {
322 log.Debugw("GetChildDeviceWithProxyAddress", log.Fields{"proxyAddress": proxyAddress})
323
324 var parentDevice *voltha.Device
325 var err error
326 if parentDevice, err = dMgr.GetDevice(proxyAddress.DeviceId); err != nil {
327 return nil, status.Errorf(codes.Aborted, "%s", err.Error())
328 }
329 var childDeviceIds []string
330 if childDeviceIds, err = dMgr.getAllChildDeviceIds(parentDevice); err != nil {
331 return nil, status.Errorf(codes.Aborted, "%s", err.Error())
332 }
333 if len(childDeviceIds) == 0 {
334 log.Debugw("no-child-devices", log.Fields{"parentDeviceId": parentDevice.Id})
335 return nil, status.Errorf(codes.NotFound, "%s", proxyAddress)
336 }
337
338 var foundChildDevice *voltha.Device
339 for _, childDeviceId := range childDeviceIds {
340 if searchDevice, err := dMgr.GetDevice(childDeviceId); err == nil {
341 if searchDevice.ProxyAddress == proxyAddress {
342 foundChildDevice = searchDevice
343 break
344 }
345 }
346 }
347
348 if foundChildDevice != nil {
349 log.Debugw("child-device-found", log.Fields{"proxyAddress": proxyAddress})
350 return foundChildDevice, nil
351 }
352
353 log.Warnw("child-device-not-found", log.Fields{"proxyAddress": proxyAddress})
354 return nil, status.Errorf(codes.NotFound, "%s", proxyAddress)
355}
356
khenaidoo297cd252019-02-07 22:10:23 -0500357func (dMgr *DeviceManager) IsDeviceInCache(id string) bool {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400358 dMgr.lockDeviceAgentsMap.RLock()
359 defer dMgr.lockDeviceAgentsMap.RUnlock()
khenaidoo297cd252019-02-07 22:10:23 -0500360 _, exist := dMgr.deviceAgents[id]
361 return exist
362}
363
khenaidoo19d7b632018-10-30 10:49:50 -0400364func (dMgr *DeviceManager) IsRootDevice(id string) (bool, error) {
khenaidoo2c6a0992019-04-29 13:46:56 -0400365 dMgr.lockRootDeviceMap.RLock()
366 defer dMgr.lockRootDeviceMap.RUnlock()
367 if exist := dMgr.rootDevices[id]; exist {
368 return dMgr.rootDevices[id], nil
khenaidoo19d7b632018-10-30 10:49:50 -0400369 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400370 return false, nil
khenaidoo19d7b632018-10-30 10:49:50 -0400371}
372
Stephane Barbarieaa467942019-02-06 14:09:44 -0500373// ListDevices retrieves the latest devices from the data model
khenaidoob9203542018-09-17 22:56:37 -0400374func (dMgr *DeviceManager) ListDevices() (*voltha.Devices, error) {
khenaidoo92e62c52018-10-03 14:02:54 -0400375 log.Debug("ListDevices")
khenaidoob9203542018-09-17 22:56:37 -0400376 result := &voltha.Devices{}
Stephane Barbarieaa467942019-02-06 14:09:44 -0500377 if devices := dMgr.clusterDataProxy.List("/devices", 0, false, ""); devices != nil {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500378 for _, device := range devices.([]interface{}) {
khenaidoo297cd252019-02-07 22:10:23 -0500379 // If device is not in memory then set it up
380 if !dMgr.IsDeviceInCache(device.(*voltha.Device).Id) {
khenaidoo6d62c002019-05-15 21:57:03 -0400381 log.Debugw("loading-device-from-Model", log.Fields{"id": device.(*voltha.Device).Id})
khenaidoo2c6a0992019-04-29 13:46:56 -0400382 agent := newDeviceAgent(dMgr.adapterProxy, device.(*voltha.Device), dMgr, dMgr.clusterDataProxy, dMgr.defaultTimeout)
khenaidoo297cd252019-02-07 22:10:23 -0500383 if err := agent.start(nil, true); err != nil {
384 log.Warnw("failure-starting-agent", log.Fields{"deviceId": device.(*voltha.Device).Id})
385 agent.stop(nil)
386 } else {
387 dMgr.addDeviceAgentToMap(agent)
388 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500389 }
390 result.Items = append(result.Items, device.(*voltha.Device))
khenaidoob9203542018-09-17 22:56:37 -0400391 }
392 }
khenaidoo6d62c002019-05-15 21:57:03 -0400393 log.Debugw("ListDevices-end", log.Fields{"len": len(result.Items)})
khenaidoob9203542018-09-17 22:56:37 -0400394 return result, nil
395}
396
khenaidoo6d62c002019-05-15 21:57:03 -0400397//getDeviceFromModelretrieves the device data from the model.
398func (dMgr *DeviceManager) getDeviceFromModel(deviceId string) (*voltha.Device, error) {
399 if device := dMgr.clusterDataProxy.Get("/devices/"+deviceId, 0, false, ""); device != nil {
400 if d, ok := device.(*voltha.Device); ok {
401 return d, nil
402 }
403 }
404 return nil, status.Error(codes.NotFound, deviceId)
405}
406
khenaidoo297cd252019-02-07 22:10:23 -0500407// loadDevice loads the deviceId in memory, if not present
408func (dMgr *DeviceManager) loadDevice(deviceId string) (*DeviceAgent, error) {
409 log.Debugw("loading-device", log.Fields{"deviceId": deviceId})
410 // Sanity check
411 if deviceId == "" {
412 return nil, status.Error(codes.InvalidArgument, "deviceId empty")
413 }
414 if !dMgr.IsDeviceInCache(deviceId) {
khenaidoo6d62c002019-05-15 21:57:03 -0400415 // Proceed with the loading only if the device exist in the Model (could have been deleted)
416 if device, err := dMgr.getDeviceFromModel(deviceId); err == nil {
417 agent := newDeviceAgent(dMgr.adapterProxy, device, dMgr, dMgr.clusterDataProxy, dMgr.defaultTimeout)
418 if err := agent.start(nil, true); err != nil {
419 agent.stop(nil)
420 return nil, err
421 }
422 dMgr.addDeviceAgentToMap(agent)
423 } else {
424 return nil, status.Error(codes.NotFound, deviceId)
khenaidoo297cd252019-02-07 22:10:23 -0500425 }
khenaidoo297cd252019-02-07 22:10:23 -0500426 }
427 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
428 return agent, nil
429 }
Matt Jeanneret4e241952019-02-28 11:16:04 -0500430 return nil, status.Error(codes.NotFound, deviceId) // This should not happen
khenaidoo297cd252019-02-07 22:10:23 -0500431}
432
433// loadRootDeviceParentAndChildren loads the children and parents of a root device in memory
434func (dMgr *DeviceManager) loadRootDeviceParentAndChildren(device *voltha.Device) error {
435 log.Debugw("loading-parent-and-children", log.Fields{"deviceId": device.Id})
436 if device.Root {
437 // Scenario A
438 if device.ParentId != "" {
439 // Load logical device if needed.
440 if err := dMgr.logicalDeviceMgr.load(device.ParentId); err != nil {
441 log.Warnw("failure-loading-logical-device", log.Fields{"lDeviceId": device.ParentId})
442 }
443 } else {
444 log.Debugw("no-parent-to-load", log.Fields{"deviceId": device.Id})
445 }
446 // Load all child devices, if needed
447 if childDeviceIds, err := dMgr.getAllChildDeviceIds(device); err == nil {
448 for _, childDeviceId := range childDeviceIds {
449 if _, err := dMgr.loadDevice(childDeviceId); err != nil {
450 log.Warnw("failure-loading-device", log.Fields{"deviceId": childDeviceId})
451 return err
452 }
453 }
454 log.Debugw("loaded-children", log.Fields{"deviceId": device.Id, "numChildren": len(childDeviceIds)})
455 } else {
456 log.Debugw("no-child-to-load", log.Fields{"deviceId": device.Id})
457 }
458 }
459 return nil
460}
461
462// load loads the deviceId in memory, if not present, and also loads its accompanying parents and children. Loading
463// in memory is for improved performance. It is not imperative that a device needs to be in memory when a request
464// acting on the device is received by the core. In such a scenario, the Core will load the device in memory first
465// and the proceed with the request.
466func (dMgr *DeviceManager) load(deviceId string) error {
467 log.Debug("load...")
468 // First load the device - this may fail in case the device was deleted intentionally by the other core
469 var dAgent *DeviceAgent
470 var err error
471 if dAgent, err = dMgr.loadDevice(deviceId); err != nil {
khenaidoo297cd252019-02-07 22:10:23 -0500472 return err
473 }
474 // Get the loaded device details
475 var device *voltha.Device
476 if device, err = dAgent.getDevice(); err != nil {
477 return err
478 }
479
480 // If the device is in Pre-provisioning or deleted state stop here
481 if device.AdminState == voltha.AdminState_PREPROVISIONED || device.AdminState == voltha.AdminState_DELETED {
482 return nil
483 }
484
485 // Now we face two scenarios
486 if device.Root {
487 // Load all children as well as the parent of this device (logical_device)
488 if err := dMgr.loadRootDeviceParentAndChildren(device); err != nil {
489 log.Warnw("failure-loading-device-parent-and-children", log.Fields{"deviceId": deviceId})
490 return err
491 }
492 log.Debugw("successfully-loaded-parent-and-children", log.Fields{"deviceId": deviceId})
493 } else {
494 // Scenario B - use the parentId of that device (root device) to trigger the loading
495 if device.ParentId != "" {
496 return dMgr.load(device.ParentId)
497 }
498 }
499 return nil
500}
501
khenaidoo7ccedd52018-12-14 16:48:54 -0500502// ListDeviceIds retrieves the latest device IDs information from the data model (memory data only)
503func (dMgr *DeviceManager) ListDeviceIds() (*voltha.IDs, error) {
504 log.Debug("ListDeviceIDs")
505 // Report only device IDs that are in the device agent map
506 return dMgr.listDeviceIdsFromMap(), nil
507}
508
509//ReconcileDevices is a request to a voltha core to managed a list of devices based on their IDs
510func (dMgr *DeviceManager) ReconcileDevices(ctx context.Context, ids *voltha.IDs, ch chan interface{}) {
511 log.Debug("ReconcileDevices")
512 var res interface{}
513 if ids != nil {
514 toReconcile := len(ids.Items)
515 reconciled := 0
516 for _, id := range ids.Items {
517 // Act on the device only if its not present in the agent map
khenaidoo297cd252019-02-07 22:10:23 -0500518 if !dMgr.IsDeviceInCache(id.Id) {
khenaidoo7ccedd52018-12-14 16:48:54 -0500519 // Device Id not in memory
520 log.Debugw("reconciling-device", log.Fields{"id": id.Id})
khenaidoo6d62c002019-05-15 21:57:03 -0400521 // Proceed with the loading only if the device exist in the Model (could have been deleted)
522 if device, err := dMgr.getDeviceFromModel(id.Id); err == nil {
523 agent := newDeviceAgent(dMgr.adapterProxy, device, dMgr, dMgr.clusterDataProxy, dMgr.defaultTimeout)
524 if err := agent.start(nil, true); err != nil {
525 log.Warnw("failure-loading-device", log.Fields{"deviceId": id.Id})
526 agent.stop(nil)
527 } else {
528 dMgr.addDeviceAgentToMap(agent)
529 reconciled += 1
530 }
khenaidoo7ccedd52018-12-14 16:48:54 -0500531 } else {
khenaidoo297cd252019-02-07 22:10:23 -0500532 reconciled += 1
khenaidoo7ccedd52018-12-14 16:48:54 -0500533 }
khenaidoo7ccedd52018-12-14 16:48:54 -0500534 }
535 }
536 if toReconcile != reconciled {
537 res = status.Errorf(codes.DataLoss, "less-device-reconciled:%d/%d", reconciled, toReconcile)
538 }
539 } else {
540 res = status.Errorf(codes.InvalidArgument, "empty-list-of-ids")
541 }
542 sendResponse(ctx, ch, res)
543}
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500544
khenaidoob9203542018-09-17 22:56:37 -0400545func (dMgr *DeviceManager) updateDevice(device *voltha.Device) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400546 log.Debugw("updateDevice", log.Fields{"deviceid": device.Id, "device": device})
khenaidoob9203542018-09-17 22:56:37 -0400547 if agent := dMgr.getDeviceAgent(device.Id); agent != nil {
548 return agent.updateDevice(device)
549 }
550 return status.Errorf(codes.NotFound, "%s", device.Id)
551}
552
553func (dMgr *DeviceManager) addPort(deviceId string, port *voltha.Port) error {
554 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
khenaidoo92e62c52018-10-03 14:02:54 -0400555 if err := agent.addPort(port); err != nil {
556 return err
557 }
558 // Setup peer ports
559 meAsPeer := &voltha.Port_PeerPort{DeviceId: deviceId, PortNo: port.PortNo}
560 for _, peerPort := range port.Peers {
561 if agent := dMgr.getDeviceAgent(peerPort.DeviceId); agent != nil {
562 if err := agent.addPeerPort(meAsPeer); err != nil {
563 log.Errorw("failed-to-add-peer", log.Fields{"peer-device-id": peerPort.DeviceId})
564 return err
565 }
566 }
567 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400568 // Notify the logical device manager to setup a logical port, if needed. If the added port is an NNI or UNI
569 // then a logical port will be added to the logical device and the device graph generated. If the port is a
570 // PON port then only the device graph will be generated.
571 if device, err := dMgr.GetDevice(deviceId); err == nil {
572 go dMgr.logicalDeviceMgr.updateLogicalPort(device, port)
573 } else {
574 log.Errorw("failed-to-retrieve-device", log.Fields{"deviceId": deviceId})
575 return err
khenaidoofc1314d2019-03-14 09:34:21 -0400576 }
khenaidoo92e62c52018-10-03 14:02:54 -0400577 return nil
578 } else {
579 return status.Errorf(codes.NotFound, "%s", deviceId)
khenaidoob9203542018-09-17 22:56:37 -0400580 }
khenaidoob9203542018-09-17 22:56:37 -0400581}
582
khenaidoo0a822f92019-05-08 15:15:57 -0400583func (dMgr *DeviceManager) deletePeerPorts(fromDeviceId string, deviceId string) error {
584 log.Debugw("deletePeerPorts", log.Fields{"fromDeviceId": fromDeviceId, "deviceid": deviceId})
585 if agent := dMgr.getDeviceAgent(fromDeviceId); agent != nil {
586 return agent.deletePeerPorts(deviceId)
587 }
588 return status.Errorf(codes.NotFound, "%s", deviceId)
589}
590
khenaidoo2c6a0992019-04-29 13:46:56 -0400591func (dMgr *DeviceManager) addFlowsAndGroups(deviceId string, flows []*ofp.OfpFlowStats, groups []*ofp.OfpGroupEntry) error {
592 log.Debugw("addFlowsAndGroups", log.Fields{"deviceid": deviceId})
khenaidoo19d7b632018-10-30 10:49:50 -0400593 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
khenaidoo2c6a0992019-04-29 13:46:56 -0400594 return agent.addFlowsAndGroups(flows, groups)
595 //go agent.addFlowsAndGroups(flows, groups)
596 //return nil
khenaidoo19d7b632018-10-30 10:49:50 -0400597 }
598 return status.Errorf(codes.NotFound, "%s", deviceId)
599}
600
khenaidoob9203542018-09-17 22:56:37 -0400601func (dMgr *DeviceManager) updatePmConfigs(deviceId string, pmConfigs *voltha.PmConfigs) error {
602 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
603 return agent.updatePmConfigs(pmConfigs)
604 }
605 return status.Errorf(codes.NotFound, "%s", deviceId)
606}
607
khenaidoo79232702018-12-04 11:00:41 -0500608func (dMgr *DeviceManager) getSwitchCapability(ctx context.Context, deviceId string) (*ic.SwitchCapability, error) {
khenaidoo92e62c52018-10-03 14:02:54 -0400609 log.Debugw("getSwitchCapability", log.Fields{"deviceid": deviceId})
khenaidoob9203542018-09-17 22:56:37 -0400610 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
611 return agent.getSwitchCapability(ctx)
612 }
613 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
614}
615
khenaidoo92e62c52018-10-03 14:02:54 -0400616func (dMgr *DeviceManager) getPorts(ctx context.Context, deviceId string, portType voltha.Port_PortType) (*voltha.Ports, error) {
617 log.Debugw("getPorts", log.Fields{"deviceid": deviceId, "portType": portType})
khenaidoob9203542018-09-17 22:56:37 -0400618 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
khenaidoo92e62c52018-10-03 14:02:54 -0400619 return agent.getPorts(ctx, portType), nil
khenaidoob9203542018-09-17 22:56:37 -0400620 }
621 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
khenaidoo92e62c52018-10-03 14:02:54 -0400622
khenaidoob9203542018-09-17 22:56:37 -0400623}
624
khenaidoo79232702018-12-04 11:00:41 -0500625func (dMgr *DeviceManager) getPortCapability(ctx context.Context, deviceId string, portNo uint32) (*ic.PortCapability, error) {
khenaidoo92e62c52018-10-03 14:02:54 -0400626 log.Debugw("getPortCapability", log.Fields{"deviceid": deviceId})
khenaidoob9203542018-09-17 22:56:37 -0400627 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
628 return agent.getPortCapability(ctx, portNo)
629 }
630 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
631}
632
khenaidoo92e62c52018-10-03 14:02:54 -0400633func (dMgr *DeviceManager) updateDeviceStatus(deviceId string, operStatus voltha.OperStatus_OperStatus, connStatus voltha.ConnectStatus_ConnectStatus) error {
634 log.Debugw("updateDeviceStatus", log.Fields{"deviceid": deviceId, "operStatus": operStatus, "connStatus": connStatus})
khenaidoob9203542018-09-17 22:56:37 -0400635 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
khenaidoo92e62c52018-10-03 14:02:54 -0400636 return agent.updateDeviceStatus(operStatus, connStatus)
637 }
638 return status.Errorf(codes.NotFound, "%s", deviceId)
639}
640
khenaidoo4d4802d2018-10-04 21:59:49 -0400641func (dMgr *DeviceManager) updateChildrenStatus(deviceId string, operStatus voltha.OperStatus_OperStatus, connStatus voltha.ConnectStatus_ConnectStatus) error {
642 log.Debugw("updateChildrenStatus", log.Fields{"parentDeviceid": deviceId, "operStatus": operStatus, "connStatus": connStatus})
643 var parentDevice *voltha.Device
644 var err error
khenaidoo19d7b632018-10-30 10:49:50 -0400645 if parentDevice, err = dMgr.GetDevice(deviceId); err != nil {
khenaidoo4d4802d2018-10-04 21:59:49 -0400646 return status.Errorf(codes.Aborted, "%s", err.Error())
647 }
648 var childDeviceIds []string
649 if childDeviceIds, err = dMgr.getAllChildDeviceIds(parentDevice); err != nil {
650 return status.Errorf(codes.Aborted, "%s", err.Error())
651 }
652 if len(childDeviceIds) == 0 {
653 log.Debugw("no-child-device", log.Fields{"parentDeviceId": parentDevice.Id})
654 }
655 for _, childDeviceId := range childDeviceIds {
656 if agent := dMgr.getDeviceAgent(childDeviceId); agent != nil {
657 if err = agent.updateDeviceStatus(operStatus, connStatus); err != nil {
658 return status.Errorf(codes.Aborted, "childDevice:%s, error:%s", childDeviceId, err.Error())
659 }
660 }
661 }
662 return nil
663}
664
khenaidoo92e62c52018-10-03 14:02:54 -0400665func (dMgr *DeviceManager) updatePortState(deviceId string, portType voltha.Port_PortType, portNo uint32, operStatus voltha.OperStatus_OperStatus) error {
666 log.Debugw("updatePortState", log.Fields{"deviceid": deviceId, "portType": portType, "portNo": portNo, "operStatus": operStatus})
667 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
668 return agent.updatePortState(portType, portNo, operStatus)
khenaidoob9203542018-09-17 22:56:37 -0400669 }
670 return status.Errorf(codes.NotFound, "%s", deviceId)
671}
672
khenaidoo0a822f92019-05-08 15:15:57 -0400673func (dMgr *DeviceManager) deleteAllPorts(deviceId string) error {
674 log.Debugw("DeleteAllPorts", log.Fields{"deviceid": deviceId})
675 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
676 if err := agent.deleteAllPorts(); err != nil {
677 return err
678 }
679 // Notify the logical device manager to remove all logical ports, if needed.
680 // At this stage the device itself may gave been deleted already at a deleteAllPorts
681 // typically is part of a device deletion phase.
682 if device, err := dMgr.GetDevice(deviceId); err == nil {
683 go dMgr.logicalDeviceMgr.deleteAllLogicalPorts(device)
684 } else {
685 log.Warnw("failed-to-retrieve-device", log.Fields{"deviceId": deviceId})
686 return err
687 }
688 return nil
689 }
690 return status.Errorf(codes.NotFound, "%s", deviceId)
691}
692
khenaidoo3ab34882019-05-02 21:33:30 -0400693//updatePortsState updates all ports on the device
694func (dMgr *DeviceManager) updatePortsState(deviceId string, state voltha.OperStatus_OperStatus) error {
695 log.Debugw("updatePortsState", log.Fields{"deviceid": deviceId})
696
697 var adminState voltha.AdminState_AdminState
698 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
699 switch state {
700 case voltha.OperStatus_ACTIVE:
701 adminState = voltha.AdminState_ENABLED
702 if err := agent.enablePorts(); err != nil {
703 log.Warnw("enable-all-ports-failed", log.Fields{"deviceId": deviceId, "error": err})
704 return err
705 }
706 case voltha.OperStatus_UNKNOWN:
707 adminState = voltha.AdminState_DISABLED
708 if err := agent.disablePorts(); err != nil {
709 log.Warnw("disable-all-ports-failed", log.Fields{"deviceId": deviceId, "error": err})
710 return err
711 }
712 default:
713 return status.Error(codes.Unimplemented, "state-change-not-implemented")
714 }
715 // Notify the logical device about the state change
716 if device, err := dMgr.GetDevice(deviceId); err != nil {
717 log.Warnw("non-existent-device", log.Fields{"deviceId": deviceId, "error": err})
718 return err
719 } else {
720 if err := dMgr.logicalDeviceMgr.updatePortsState(device, adminState); err != nil {
721 log.Warnw("failed-updating-ports-state", log.Fields{"deviceId": deviceId, "error": err})
722 return err
723 }
724 return nil
725 }
726 }
727 return status.Errorf(codes.NotFound, "%s", deviceId)
728}
729
Matt Jeanneret4e241952019-02-28 11:16:04 -0500730func (dMgr *DeviceManager) childDeviceDetected(parentDeviceId string, parentPortNo int64, deviceType string,
731 channelId int64, vendorId string, serialNumber string, onuId int64) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400732 log.Debugw("childDeviceDetected", log.Fields{"parentDeviceId": parentDeviceId})
khenaidoob9203542018-09-17 22:56:37 -0400733
734 // Create the ONU device
735 childDevice := &voltha.Device{}
khenaidoob9203542018-09-17 22:56:37 -0400736 childDevice.Type = deviceType
737 childDevice.ParentId = parentDeviceId
738 childDevice.ParentPortNo = uint32(parentPortNo)
Matt Jeanneret4e241952019-02-28 11:16:04 -0500739 childDevice.VendorId = vendorId
740 childDevice.SerialNumber = serialNumber
khenaidoob9203542018-09-17 22:56:37 -0400741 childDevice.Root = false
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400742
743 //Get parent device type
744 parent, err := dMgr.GetDevice(parentDeviceId)
745 if err != nil {
khenaidoo43c82122018-11-22 18:38:28 -0500746 log.Error("no-parent-found", log.Fields{"parentId": parentDeviceId})
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400747 return status.Errorf(codes.NotFound, "%s", parentDeviceId)
748 }
749
Matt Jeanneret4e241952019-02-28 11:16:04 -0500750 if _, err := dMgr.GetChildDevice(parentDeviceId, serialNumber, onuId, parentPortNo); err == nil {
751 log.Warnw("child-device-exists", log.Fields{"parentId": parentDeviceId, "serialNumber": serialNumber})
752 return status.Errorf(codes.AlreadyExists, "%s", serialNumber)
753 }
754
755 childDevice.ProxyAddress = &voltha.Device_ProxyAddress{DeviceId: parentDeviceId, DeviceType: parent.Type, ChannelId: uint32(channelId), OnuId: uint32(onuId)}
khenaidoob9203542018-09-17 22:56:37 -0400756
757 // Create and start a device agent for that device
khenaidoo2c6a0992019-04-29 13:46:56 -0400758 agent := newDeviceAgent(dMgr.adapterProxy, childDevice, dMgr, dMgr.clusterDataProxy, dMgr.defaultTimeout)
khenaidoob9203542018-09-17 22:56:37 -0400759 dMgr.addDeviceAgentToMap(agent)
khenaidoo297cd252019-02-07 22:10:23 -0500760 agent.start(nil, false)
khenaidoob9203542018-09-17 22:56:37 -0400761
762 // Activate the child device
khenaidoo92e62c52018-10-03 14:02:54 -0400763 if agent := dMgr.getDeviceAgent(agent.deviceId); agent != nil {
khenaidoo79232702018-12-04 11:00:41 -0500764 go agent.enableDevice(nil)
khenaidoob9203542018-09-17 22:56:37 -0400765 }
766
khenaidoo79232702018-12-04 11:00:41 -0500767 // Publish on the messaging bus that we have discovered new devices
khenaidoo19374072018-12-11 11:05:15 -0500768 go dMgr.kafkaICProxy.DeviceDiscovered(agent.deviceId, deviceType, parentDeviceId, dMgr.coreInstanceId)
khenaidoo79232702018-12-04 11:00:41 -0500769
khenaidoob9203542018-09-17 22:56:37 -0400770 return nil
771}
772
773func (dMgr *DeviceManager) processTransition(previous *voltha.Device, current *voltha.Device) error {
774 // This will be triggered on every update to the device.
khenaidoo92e62c52018-10-03 14:02:54 -0400775 handlers := dMgr.stateTransitions.GetTransitionHandler(previous, current)
776 if handlers == nil {
khenaidoo43c82122018-11-22 18:38:28 -0500777 log.Debugw("no-op-transition", log.Fields{"deviceId": current.Id})
khenaidoo92e62c52018-10-03 14:02:54 -0400778 return nil
khenaidoob9203542018-09-17 22:56:37 -0400779 }
khenaidoo0a822f92019-05-08 15:15:57 -0400780 log.Debugw("handler-found", log.Fields{"num-handlers": len(handlers), "isParent": current.Root})
khenaidoo92e62c52018-10-03 14:02:54 -0400781 for _, handler := range handlers {
782 log.Debugw("running-handler", log.Fields{"handler": funcName(handler)})
783 if err := handler(current); err != nil {
khenaidoo6d62c002019-05-15 21:57:03 -0400784 log.Warnw("handler-failed", log.Fields{"handler": funcName(handler), "error": err})
khenaidoo92e62c52018-10-03 14:02:54 -0400785 return err
786 }
787 }
khenaidoob9203542018-09-17 22:56:37 -0400788 return nil
789}
790
khenaidoofdbad6e2018-11-06 22:26:38 -0500791func (dMgr *DeviceManager) packetOut(deviceId string, outPort uint32, packet *ofp.OfpPacketOut) error {
792 log.Debugw("packetOut", log.Fields{"deviceId": deviceId, "outPort": outPort})
793 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
794 return agent.packetOut(outPort, packet)
795 }
796 return status.Errorf(codes.NotFound, "%s", deviceId)
797}
798
khenaidoo297cd252019-02-07 22:10:23 -0500799func (dMgr *DeviceManager) PacketIn(deviceId string, port uint32, transactionId string, packet []byte) error {
khenaidoofdbad6e2018-11-06 22:26:38 -0500800 log.Debugw("PacketIn", log.Fields{"deviceId": deviceId, "port": port})
801 // Get the logical device Id based on the deviceId
802 var device *voltha.Device
803 var err error
804 if device, err = dMgr.GetDevice(deviceId); err != nil {
805 log.Errorw("device-not-found", log.Fields{"deviceId": deviceId})
806 return err
807 }
khenaidoo43c82122018-11-22 18:38:28 -0500808 if !device.Root {
khenaidoofdbad6e2018-11-06 22:26:38 -0500809 log.Errorw("device-not-root", log.Fields{"deviceId": deviceId})
810 return status.Errorf(codes.FailedPrecondition, "%s", deviceId)
811 }
812
khenaidoo297cd252019-02-07 22:10:23 -0500813 if err := dMgr.logicalDeviceMgr.packetIn(device.ParentId, port, transactionId, packet); err != nil {
khenaidoofdbad6e2018-11-06 22:26:38 -0500814 return err
815 }
816 return nil
817}
818
khenaidoo0a822f92019-05-08 15:15:57 -0400819func (dMgr *DeviceManager) CreateLogicalDevice(cDevice *voltha.Device) error {
820 log.Info("CreateLogicalDevice")
khenaidoob9203542018-09-17 22:56:37 -0400821 var logicalId *string
822 var err error
khenaidoo4d4802d2018-10-04 21:59:49 -0400823 if logicalId, err = dMgr.logicalDeviceMgr.createLogicalDevice(nil, cDevice); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400824 log.Warnw("createlogical-device-error", log.Fields{"device": cDevice})
825 return err
826 }
827 // Update the parent device with the logical id
828 dMgr.UpdateDeviceAttribute(cDevice.Id, "ParentId", *logicalId)
829 return nil
830}
831
khenaidoo0a822f92019-05-08 15:15:57 -0400832func (dMgr *DeviceManager) DeleteLogicalDevice(cDevice *voltha.Device) error {
833 log.Info("DeleteLogicalDevice")
khenaidoo92e62c52018-10-03 14:02:54 -0400834 var err error
khenaidoo4d4802d2018-10-04 21:59:49 -0400835 if err = dMgr.logicalDeviceMgr.deleteLogicalDevice(nil, cDevice); err != nil {
khenaidoo92e62c52018-10-03 14:02:54 -0400836 log.Warnw("deleteLogical-device-error", log.Fields{"deviceId": cDevice.Id})
837 return err
838 }
839 // Remove the logical device Id from the parent device
840 logicalId := ""
841 dMgr.UpdateDeviceAttribute(cDevice.Id, "ParentId", logicalId)
842 return nil
843}
844
khenaidoo0a822f92019-05-08 15:15:57 -0400845func (dMgr *DeviceManager) DeleteLogicalPort(device *voltha.Device) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400846 log.Info("deleteLogicalPort")
847 var err error
khenaidoo19d7b632018-10-30 10:49:50 -0400848 // Get the logical port associated with this device
849 var lPortId *voltha.LogicalPortId
850 if lPortId, err = dMgr.logicalDeviceMgr.getLogicalPortId(device); err != nil {
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400851 log.Warnw("getLogical-port-error", log.Fields{"deviceId": device.Id, "error": err})
khenaidoo19d7b632018-10-30 10:49:50 -0400852 return err
853 }
854 if err = dMgr.logicalDeviceMgr.deleteLogicalPort(nil, lPortId); err != nil {
855 log.Warnw("deleteLogical-port-error", log.Fields{"deviceId": device.Id})
khenaidoo92e62c52018-10-03 14:02:54 -0400856 return err
857 }
khenaidoo92e62c52018-10-03 14:02:54 -0400858 return nil
859}
860
khenaidoo0a822f92019-05-08 15:15:57 -0400861func (dMgr *DeviceManager) DeleteLogicalPorts(device *voltha.Device) error {
862 log.Info("deleteLogicalPorts")
863 if err := dMgr.logicalDeviceMgr.deleteLogicalPorts(device.Id); err != nil {
864 log.Warnw("deleteLogical-ports-error", log.Fields{"deviceId": device.Id})
865 return err
866 }
867 return nil
868}
869
khenaidoo92e62c52018-10-03 14:02:54 -0400870func (dMgr *DeviceManager) getParentDevice(childDevice *voltha.Device) *voltha.Device {
871 // Sanity check
872 if childDevice.Root {
873 // childDevice is the parent device
874 return childDevice
875 }
khenaidoo19d7b632018-10-30 10:49:50 -0400876 parentDevice, _ := dMgr.GetDevice(childDevice.ParentId)
khenaidoo92e62c52018-10-03 14:02:54 -0400877 return parentDevice
878}
879
khenaidoo0a822f92019-05-08 15:15:57 -0400880//childDevicesLost is invoked by an adapter to indicate that a parent device is in a state (Disabled) where it
881//cannot manage the child devices. This will trigger the Core to disable all the child devices.
882func (dMgr *DeviceManager) childDevicesLost(parentDeviceId string) error {
883 log.Debug("childDevicesLost")
884 var err error
885 var parentDevice *voltha.Device
886 if parentDevice, err = dMgr.GetDevice(parentDeviceId); err != nil {
887 log.Warnw("failed-getting-device", log.Fields{"deviceId": parentDeviceId, "error": err})
888 return err
889 }
890 return dMgr.DisableAllChildDevices(parentDevice)
891}
892
893//childDevicesDetected is invoked by an adapter when child devices are found, typically after after a
894// disable/enable sequence. This will trigger the Core to Enable all the child devices of that parent.
895func (dMgr *DeviceManager) childDevicesDetected(parentDeviceId string) error {
896 log.Debug("childDevicesDetected")
897 var err error
898 var parentDevice *voltha.Device
899 var childDeviceIds []string
900
901 if parentDevice, err = dMgr.GetDevice(parentDeviceId); err != nil {
902 log.Warnw("failed-getting-device", log.Fields{"deviceId": parentDeviceId, "error": err})
903 return err
904 }
905
906 if childDeviceIds, err = dMgr.getAllChildDeviceIds(parentDevice); err != nil {
907 return status.Errorf(codes.NotFound, "%s", parentDevice.Id)
908 }
909 if len(childDeviceIds) == 0 {
910 log.Debugw("no-child-device", log.Fields{"parentDeviceId": parentDevice.Id})
911 }
912 allChildDisable := true
913 for _, childDeviceId := range childDeviceIds {
914 if agent := dMgr.getDeviceAgent(childDeviceId); agent != nil {
915 if err = agent.enableDevice(nil); err != nil {
916 log.Errorw("failure-enable-device", log.Fields{"deviceId": childDeviceId, "error": err.Error()})
917 allChildDisable = false
918 }
919 }
920 }
921 if !allChildDisable {
922 return err
923 }
924 return nil
925}
926
khenaidoo4d4802d2018-10-04 21:59:49 -0400927/*
928All the functions below are callback functions where they are invoked with the latest and previous data. We can
929therefore use the data as is without trying to get the latest from the model.
930*/
931
khenaidoo0a822f92019-05-08 15:15:57 -0400932//DisableAllChildDevices is invoked as a callback when the parent device is disabled
933func (dMgr *DeviceManager) DisableAllChildDevices(parentDevice *voltha.Device) error {
934 log.Debug("DisableAllChildDevices")
khenaidoo92e62c52018-10-03 14:02:54 -0400935 var childDeviceIds []string
936 var err error
khenaidoo4d4802d2018-10-04 21:59:49 -0400937 if childDeviceIds, err = dMgr.getAllChildDeviceIds(parentDevice); err != nil {
938 return status.Errorf(codes.NotFound, "%s", parentDevice.Id)
khenaidoo92e62c52018-10-03 14:02:54 -0400939 }
940 if len(childDeviceIds) == 0 {
khenaidoo4d4802d2018-10-04 21:59:49 -0400941 log.Debugw("no-child-device", log.Fields{"parentDeviceId": parentDevice.Id})
khenaidoo92e62c52018-10-03 14:02:54 -0400942 }
khenaidoo4d4802d2018-10-04 21:59:49 -0400943 allChildDisable := true
khenaidoo92e62c52018-10-03 14:02:54 -0400944 for _, childDeviceId := range childDeviceIds {
945 if agent := dMgr.getDeviceAgent(childDeviceId); agent != nil {
946 if err = agent.disableDevice(nil); err != nil {
947 log.Errorw("failure-disable-device", log.Fields{"deviceId": childDeviceId, "error": err.Error()})
khenaidoo4d4802d2018-10-04 21:59:49 -0400948 allChildDisable = false
khenaidoo92e62c52018-10-03 14:02:54 -0400949 }
950 }
951 }
khenaidoo4d4802d2018-10-04 21:59:49 -0400952 if !allChildDisable {
953 return err
954 }
khenaidoo92e62c52018-10-03 14:02:54 -0400955 return nil
956}
957
khenaidoo0a822f92019-05-08 15:15:57 -0400958//DeleteAllChildDevices is invoked as a callback when the parent device is deleted
959func (dMgr *DeviceManager) DeleteAllChildDevices(parentDevice *voltha.Device) error {
960 log.Debug("DeleteAllChildDevices")
khenaidoo4d4802d2018-10-04 21:59:49 -0400961 var childDeviceIds []string
khenaidoo92e62c52018-10-03 14:02:54 -0400962 var err error
khenaidoo4d4802d2018-10-04 21:59:49 -0400963 if childDeviceIds, err = dMgr.getAllChildDeviceIds(parentDevice); err != nil {
964 return status.Errorf(codes.NotFound, "%s", parentDevice.Id)
khenaidoo92e62c52018-10-03 14:02:54 -0400965 }
khenaidoo4d4802d2018-10-04 21:59:49 -0400966 if len(childDeviceIds) == 0 {
967 log.Debugw("no-child-device", log.Fields{"parentDeviceId": parentDevice.Id})
968 }
969 allChildDeleted := true
970 for _, childDeviceId := range childDeviceIds {
971 if agent := dMgr.getDeviceAgent(childDeviceId); agent != nil {
972 if err = agent.deleteDevice(nil); err != nil {
973 log.Errorw("failure-delete-device", log.Fields{"deviceId": childDeviceId, "error": err.Error()})
974 allChildDeleted = false
975 } else {
976 agent.stop(nil)
977 dMgr.deleteDeviceAgentToMap(agent)
978 }
979 }
980 }
981 if !allChildDeleted {
982 return err
983 }
984 return nil
985}
986
khenaidoo6d62c002019-05-15 21:57:03 -0400987//getAllDeviceIdsWithDeviceParentId returns the list of device Ids which has id as parent Id. This function uses the
988// data from the agent instead of using the data from the parent device as that data would disappear from a parent
989// device during a delete device operation.
990func (dMgr *DeviceManager) getAllDeviceIdsWithDeviceParentId(id string) []string {
991 log.Debugw("getAllAgentsWithDeviceParentId", log.Fields{"parentDeviceId": id})
992 deviceIds := make([]string, 0)
993 dMgr.lockDeviceAgentsMap.RLock()
994 defer dMgr.lockDeviceAgentsMap.RUnlock()
995 for deviceId, agent := range dMgr.deviceAgents {
996 if agent.parentId == id {
997 deviceIds = append(deviceIds, deviceId)
998 }
999 }
1000 return deviceIds
1001}
1002
khenaidoo4d4802d2018-10-04 21:59:49 -04001003//getAllChildDeviceIds is a helper method to get all the child device IDs from the device passed as parameter
1004func (dMgr *DeviceManager) getAllChildDeviceIds(parentDevice *voltha.Device) ([]string, error) {
1005 log.Debugw("getAllChildDeviceIds", log.Fields{"parentDeviceId": parentDevice.Id})
khenaidoo92e62c52018-10-03 14:02:54 -04001006 childDeviceIds := make([]string, 0)
khenaidoo4d4802d2018-10-04 21:59:49 -04001007 if parentDevice != nil {
1008 for _, port := range parentDevice.Ports {
1009 for _, peer := range port.Peers {
1010 childDeviceIds = append(childDeviceIds, peer.DeviceId)
1011 }
khenaidoo92e62c52018-10-03 14:02:54 -04001012 }
khenaidoo2c6a0992019-04-29 13:46:56 -04001013 log.Debugw("returning-getAllChildDeviceIds", log.Fields{"parentDeviceId": parentDevice.Id, "childDeviceIds": childDeviceIds})
khenaidoo92e62c52018-10-03 14:02:54 -04001014 }
1015 return childDeviceIds, nil
1016}
1017
khenaidoo297cd252019-02-07 22:10:23 -05001018//getAllChildDevices is a helper method to get all the child device IDs from the device passed as parameter
1019func (dMgr *DeviceManager) getAllChildDevices(parentDeviceId string) (*voltha.Devices, error) {
1020 log.Debugw("getAllChildDevices", log.Fields{"parentDeviceId": parentDeviceId})
1021 if parentDevice, err := dMgr.GetDevice(parentDeviceId); err == nil {
1022 childDevices := make([]*voltha.Device, 0)
1023 if childDeviceIds, er := dMgr.getAllChildDeviceIds(parentDevice); er == nil {
1024 for _, deviceId := range childDeviceIds {
1025 if d, e := dMgr.GetDevice(deviceId); e == nil && d != nil {
1026 childDevices = append(childDevices, d)
1027 }
1028 }
1029 }
1030 return &voltha.Devices{Items: childDevices}, nil
1031 }
1032 return nil, status.Errorf(codes.NotFound, "%s", parentDeviceId)
1033}
1034
khenaidoo0a822f92019-05-08 15:15:57 -04001035func (dMgr *DeviceManager) SetupUNILogicalPorts(cDevice *voltha.Device) error {
khenaidoob9203542018-09-17 22:56:37 -04001036 log.Info("addUNILogicalPort")
khenaidoofc1314d2019-03-14 09:34:21 -04001037 if err := dMgr.logicalDeviceMgr.setupUNILogicalPorts(nil, cDevice); err != nil {
khenaidoob9203542018-09-17 22:56:37 -04001038 log.Warnw("addUNILogicalPort-error", log.Fields{"device": cDevice, "err": err})
1039 return err
1040 }
1041 return nil
1042}
1043
khenaidoof5a5bfa2019-01-23 22:20:29 -05001044func (dMgr *DeviceManager) downloadImage(ctx context.Context, img *voltha.ImageDownload, ch chan interface{}) {
1045 log.Debugw("downloadImage", log.Fields{"deviceid": img.Id, "imageName": img.Name})
1046 var res interface{}
1047 var err error
1048 if agent := dMgr.getDeviceAgent(img.Id); agent != nil {
1049 if res, err = agent.downloadImage(ctx, img); err != nil {
1050 log.Debugw("downloadImage-failed", log.Fields{"err": err, "imageName": img.Name})
1051 res = err
1052 }
1053 } else {
1054 res = status.Errorf(codes.NotFound, "%s", img.Id)
1055 }
1056 sendResponse(ctx, ch, res)
1057}
1058
1059func (dMgr *DeviceManager) cancelImageDownload(ctx context.Context, img *voltha.ImageDownload, ch chan interface{}) {
1060 log.Debugw("cancelImageDownload", log.Fields{"deviceid": img.Id, "imageName": img.Name})
1061 var res interface{}
1062 var err error
1063 if agent := dMgr.getDeviceAgent(img.Id); agent != nil {
1064 if res, err = agent.cancelImageDownload(ctx, img); err != nil {
1065 log.Debugw("cancelImageDownload-failed", log.Fields{"err": err, "imageName": img.Name})
1066 res = err
1067 }
1068 } else {
1069 res = status.Errorf(codes.NotFound, "%s", img.Id)
1070 }
1071 sendResponse(ctx, ch, res)
1072}
1073
1074func (dMgr *DeviceManager) activateImage(ctx context.Context, img *voltha.ImageDownload, ch chan interface{}) {
1075 log.Debugw("activateImage", log.Fields{"deviceid": img.Id, "imageName": img.Name})
1076 var res interface{}
1077 var err error
1078 if agent := dMgr.getDeviceAgent(img.Id); agent != nil {
1079 if res, err = agent.activateImage(ctx, img); err != nil {
1080 log.Debugw("activateImage-failed", log.Fields{"err": err, "imageName": img.Name})
1081 res = err
1082 }
1083 } else {
1084 res = status.Errorf(codes.NotFound, "%s", img.Id)
1085 }
1086 sendResponse(ctx, ch, res)
1087}
1088
1089func (dMgr *DeviceManager) revertImage(ctx context.Context, img *voltha.ImageDownload, ch chan interface{}) {
1090 log.Debugw("revertImage", log.Fields{"deviceid": img.Id, "imageName": img.Name})
1091 var res interface{}
1092 var err error
1093 if agent := dMgr.getDeviceAgent(img.Id); agent != nil {
1094 if res, err = agent.revertImage(ctx, img); err != nil {
1095 log.Debugw("revertImage-failed", log.Fields{"err": err, "imageName": img.Name})
1096 res = err
1097 }
1098 } else {
1099 res = status.Errorf(codes.NotFound, "%s", img.Id)
1100 }
1101 sendResponse(ctx, ch, res)
1102}
1103
1104func (dMgr *DeviceManager) getImageDownloadStatus(ctx context.Context, img *voltha.ImageDownload, ch chan interface{}) {
1105 log.Debugw("getImageDownloadStatus", log.Fields{"deviceid": img.Id, "imageName": img.Name})
1106 var res interface{}
1107 var err error
1108 if agent := dMgr.getDeviceAgent(img.Id); agent != nil {
1109 if res, err = agent.getImageDownloadStatus(ctx, img); err != nil {
1110 log.Debugw("getImageDownloadStatus-failed", log.Fields{"err": err, "imageName": img.Name})
1111 res = err
1112 }
1113 } else {
1114 res = status.Errorf(codes.NotFound, "%s", img.Id)
1115 }
1116 sendResponse(ctx, ch, res)
1117}
1118
khenaidoof5a5bfa2019-01-23 22:20:29 -05001119func (dMgr *DeviceManager) updateImageDownload(deviceId string, img *voltha.ImageDownload) error {
1120 log.Debugw("updateImageDownload", log.Fields{"deviceid": img.Id, "imageName": img.Name})
1121 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
1122 if err := agent.updateImageDownload(img); err != nil {
1123 log.Debugw("updateImageDownload-failed", log.Fields{"err": err, "imageName": img.Name})
1124 return err
1125 }
1126 } else {
1127 return status.Errorf(codes.NotFound, "%s", img.Id)
1128 }
1129 return nil
1130}
1131
1132func (dMgr *DeviceManager) getImageDownload(ctx context.Context, img *voltha.ImageDownload) (*voltha.ImageDownload, error) {
1133 log.Debugw("getImageDownload", log.Fields{"deviceid": img.Id, "imageName": img.Name})
1134 if agent := dMgr.getDeviceAgent(img.Id); agent != nil {
1135 return agent.getImageDownload(ctx, img)
1136 }
1137 return nil, status.Errorf(codes.NotFound, "%s", img.Id)
1138}
1139
1140func (dMgr *DeviceManager) listImageDownloads(ctx context.Context, deviceId string) (*voltha.ImageDownloads, error) {
1141 log.Debugw("listImageDownloads", log.Fields{"deviceId": deviceId})
1142 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
1143 return agent.listImageDownloads(ctx, deviceId)
1144 }
1145 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
1146}
1147
khenaidoo0a822f92019-05-08 15:15:57 -04001148func (dMgr *DeviceManager) SetAdminStateToEnable(cDevice *voltha.Device) error {
1149 log.Info("SetAdminStateToEnable")
1150 if agent := dMgr.getDeviceAgent(cDevice.Id); agent != nil {
1151 return agent.updateAdminState(voltha.AdminState_ENABLED)
1152 }
1153 return status.Errorf(codes.NotFound, "%s", cDevice.Id)
1154}
1155
khenaidoo92e62c52018-10-03 14:02:54 -04001156func (dMgr *DeviceManager) activateDevice(cDevice *voltha.Device) error {
khenaidoob9203542018-09-17 22:56:37 -04001157 log.Info("activateDevice")
1158 return nil
1159}
1160
khenaidoo92e62c52018-10-03 14:02:54 -04001161func (dMgr *DeviceManager) disableDeviceHandler(cDevice *voltha.Device) error {
1162 log.Info("disableDevice-donothing")
khenaidoob9203542018-09-17 22:56:37 -04001163 return nil
1164}
1165
khenaidoo92e62c52018-10-03 14:02:54 -04001166func (dMgr *DeviceManager) abandonDevice(cDevice *voltha.Device) error {
khenaidoob9203542018-09-17 22:56:37 -04001167 log.Info("abandonDevice")
1168 return nil
1169}
1170
khenaidoo92e62c52018-10-03 14:02:54 -04001171func (dMgr *DeviceManager) reEnableDevice(cDevice *voltha.Device) error {
khenaidoob9203542018-09-17 22:56:37 -04001172 log.Info("reEnableDevice")
1173 return nil
1174}
1175
khenaidoo92e62c52018-10-03 14:02:54 -04001176func (dMgr *DeviceManager) noOp(cDevice *voltha.Device) error {
khenaidoob9203542018-09-17 22:56:37 -04001177 log.Info("noOp")
1178 return nil
1179}
1180
khenaidoo92e62c52018-10-03 14:02:54 -04001181func (dMgr *DeviceManager) notAllowed(pcDevice *voltha.Device) error {
khenaidoob9203542018-09-17 22:56:37 -04001182 log.Info("notAllowed")
khenaidoo2c6a0992019-04-29 13:46:56 -04001183 return errors.New("transition-not-allowed")
khenaidoob9203542018-09-17 22:56:37 -04001184}
1185
khenaidoo0a822f92019-05-08 15:15:57 -04001186func (dMgr *DeviceManager) NotifyInvalidTransition(pcDevice *voltha.Device) error {
1187 log.Errorw("NotifyInvalidTransition", log.Fields{"device": pcDevice.Id, "adminState": pcDevice.AdminState})
1188 //TODO: notify over kafka?
1189 return nil
1190}
1191
khenaidoob9203542018-09-17 22:56:37 -04001192func funcName(f interface{}) string {
1193 p := reflect.ValueOf(f).Pointer()
1194 rf := runtime.FuncForPC(p)
1195 return rf.Name()
1196}
1197
1198func (dMgr *DeviceManager) UpdateDeviceAttribute(deviceId string, attribute string, value interface{}) {
1199 if agent, ok := dMgr.deviceAgents[deviceId]; ok {
1200 agent.updateDeviceAttribute(attribute, value)
1201 }
1202}
1203
1204func (dMgr *DeviceManager) GetParentDeviceId(deviceId string) *string {
khenaidoo19d7b632018-10-30 10:49:50 -04001205 if device, _ := dMgr.GetDevice(deviceId); device != nil {
khenaidoo92e62c52018-10-03 14:02:54 -04001206 log.Infow("GetParentDeviceId", log.Fields{"deviceId": device.Id, "parentId": device.ParentId})
khenaidoob9203542018-09-17 22:56:37 -04001207 return &device.ParentId
1208 }
1209 return nil
1210}
serkant.uluderya334479d2019-04-10 08:26:15 -07001211
1212func (dMgr *DeviceManager) simulateAlarm(ctx context.Context, simulatereq *voltha.SimulateAlarmRequest, ch chan interface{}) {
1213 log.Debugw("simulateAlarm", log.Fields{"id": simulatereq.Id, "Indicator": simulatereq.Indicator, "IntfId": simulatereq.IntfId,
1214 "PortTypeName": simulatereq.PortTypeName, "OnuDeviceId": simulatereq.OnuDeviceId, "InverseBitErrorRate": simulatereq.InverseBitErrorRate,
1215 "Drift": simulatereq.Drift, "NewEqd": simulatereq.NewEqd, "OnuSerialNumber": simulatereq.OnuSerialNumber, "Operation": simulatereq.Operation})
1216 var res interface{}
1217 if agent := dMgr.getDeviceAgent(simulatereq.Id); agent != nil {
1218 res = agent.simulateAlarm(ctx, simulatereq)
1219 log.Debugw("SimulateAlarm-result", log.Fields{"result": res})
1220 }
1221 //TODO CLI always get successful response
1222 sendResponse(ctx, ch, res)
1223}