blob: fd69705ce513db6dfe99c32eb710ecf5aea5070c [file] [log] [blame]
Stephane Barbariea75791c2019-01-24 10:58:06 -05001/*
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"
sbarbari17d7e222019-11-05 10:02:29 -050020 "github.com/opencord/voltha-go/db/model"
Scott Baker807addd2019-10-24 15:16:21 -070021 "github.com/opencord/voltha-lib-go/v2/pkg/log"
22 "github.com/opencord/voltha-lib-go/v2/pkg/probe"
Scott Baker555307d2019-11-04 08:58:01 -080023 "github.com/opencord/voltha-protos/v2/go/voltha"
Stephane Barbariea75791c2019-01-24 10:58:06 -050024 "google.golang.org/grpc/codes"
25 "google.golang.org/grpc/status"
26 "sync"
27)
28
29type DeviceManager struct {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040030 deviceAgents sync.Map
31 logicalDeviceMgr *LogicalDeviceManager
32 clusterDataProxy *model.Proxy
33 coreInstanceId string
34 exitChannel chan int
Stephane Barbariea75791c2019-01-24 10:58:06 -050035}
36
37func newDeviceManager(cdProxy *model.Proxy, coreInstanceId string) *DeviceManager {
38 var deviceMgr DeviceManager
39 deviceMgr.exitChannel = make(chan int, 1)
Stephane Barbariea75791c2019-01-24 10:58:06 -050040 deviceMgr.coreInstanceId = coreInstanceId
41 deviceMgr.clusterDataProxy = cdProxy
Stephane Barbariea75791c2019-01-24 10:58:06 -050042 return &deviceMgr
43}
44
45func (dMgr *DeviceManager) start(ctx context.Context, logicalDeviceMgr *LogicalDeviceManager) {
46 log.Info("starting-device-manager")
47 dMgr.logicalDeviceMgr = logicalDeviceMgr
Hardik Windlassdc63dde2019-09-30 07:15:13 +000048 probe.UpdateStatusFromContext(ctx, "device-manager", probe.ServiceStatusRunning)
Stephane Barbariea75791c2019-01-24 10:58:06 -050049 log.Info("device-manager-started")
50}
51
52func (dMgr *DeviceManager) stop(ctx context.Context) {
53 log.Info("stopping-device-manager")
54 dMgr.exitChannel <- 1
Hardik Windlassdc63dde2019-09-30 07:15:13 +000055 probe.UpdateStatusFromContext(ctx, "device-manager", probe.ServiceStatusStopped)
Stephane Barbariea75791c2019-01-24 10:58:06 -050056 log.Info("device-manager-stopped")
57}
58
59func sendResponse(ctx context.Context, ch chan interface{}, result interface{}) {
60 if ctx.Err() == nil {
61 // Returned response only of the ctx has not been cancelled/timeout/etc
62 // Channel is automatically closed when a context is Done
63 ch <- result
64 log.Debugw("sendResponse", log.Fields{"result": result})
65 } else {
66 // Should the transaction be reverted back?
67 log.Debugw("sendResponse-context-error", log.Fields{"context-error": ctx.Err()})
68 }
69}
70
71func (dMgr *DeviceManager) addDeviceAgentToMap(agent *DeviceAgent) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040072 if _, exist := dMgr.deviceAgents.Load(agent.deviceId); !exist {
73 dMgr.deviceAgents.Store(agent.deviceId, agent)
Stephane Barbariea75791c2019-01-24 10:58:06 -050074 }
75}
76
77func (dMgr *DeviceManager) deleteDeviceAgentToMap(agent *DeviceAgent) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040078 dMgr.deviceAgents.Delete(agent.deviceId)
Stephane Barbariea75791c2019-01-24 10:58:06 -050079}
80
81func (dMgr *DeviceManager) getDeviceAgent(deviceId string) *DeviceAgent {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040082 if agent, ok := dMgr.deviceAgents.Load(deviceId); ok {
83 return agent.(*DeviceAgent)
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -050084 } else {
85 // Try to load into memory - loading will also create the device agent
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -050086 if err := dMgr.load(deviceId); err == nil {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040087 if agent, ok = dMgr.deviceAgents.Load(deviceId); ok {
88 return agent.(*DeviceAgent)
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -050089 }
90 }
Stephane Barbariea75791c2019-01-24 10:58:06 -050091 }
92 return nil
93}
94
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -050095// listDeviceIdsFromMap returns the list of device IDs that are in memory
Stephane Barbariea75791c2019-01-24 10:58:06 -050096func (dMgr *DeviceManager) listDeviceIdsFromMap() *voltha.IDs {
Stephane Barbariea75791c2019-01-24 10:58:06 -050097 result := &voltha.IDs{Items: make([]*voltha.ID, 0)}
Stephane Barbarieef6650d2019-07-18 12:15:09 -040098 dMgr.deviceAgents.Range(func(key, value interface{}) bool {
99 result.Items = append(result.Items, &voltha.ID{Id: key.(string)})
100 return true
101 })
Stephane Barbariea75791c2019-01-24 10:58:06 -0500102 return result
103}
104
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500105// GetDevice will returns a device, either from memory or from the dB, if present
Stephane Barbariea75791c2019-01-24 10:58:06 -0500106func (dMgr *DeviceManager) GetDevice(id string) (*voltha.Device, error) {
107 log.Debugw("GetDevice", log.Fields{"deviceid": id})
108 if agent := dMgr.getDeviceAgent(id); agent != nil {
109 return agent.getDevice()
110 }
111 return nil, status.Errorf(codes.NotFound, "%s", id)
112}
113
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500114func (dMgr *DeviceManager) IsDeviceInCache(id string) bool {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400115 _, exist := dMgr.deviceAgents.Load(id)
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500116 return exist
117}
118
Stephane Barbariea75791c2019-01-24 10:58:06 -0500119func (dMgr *DeviceManager) IsRootDevice(id string) (bool, error) {
120 device, err := dMgr.GetDevice(id)
121 if err != nil {
122 return false, err
123 }
124 return device.Root, nil
125}
126
Stephane Barbarieaa467942019-02-06 14:09:44 -0500127// ListDevices retrieves the latest devices from the data model
Stephane Barbariea75791c2019-01-24 10:58:06 -0500128func (dMgr *DeviceManager) ListDevices() (*voltha.Devices, error) {
129 log.Debug("ListDevices")
130 result := &voltha.Devices{}
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400131 if devices := dMgr.clusterDataProxy.List(context.Background(), "/devices", 0, false, ""); devices != nil {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500132 for _, device := range devices.([]interface{}) {
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500133 // If device is not in memory then set it up
134 if !dMgr.IsDeviceInCache(device.(*voltha.Device).Id) {
135 agent := newDeviceAgent(device.(*voltha.Device), dMgr, dMgr.clusterDataProxy)
136 if err := agent.start(nil, true); err != nil {
137 log.Warnw("failure-starting-agent", log.Fields{"deviceId": device.(*voltha.Device).Id})
138 agent.stop(nil)
139 } else {
140 dMgr.addDeviceAgentToMap(agent)
141 }
Stephane Barbariea75791c2019-01-24 10:58:06 -0500142 }
143 result.Items = append(result.Items, device.(*voltha.Device))
144 }
145 }
146 return result, nil
147}
148
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500149// loadDevice loads the deviceId in memory, if not present
150func (dMgr *DeviceManager) loadDevice(deviceId string) (*DeviceAgent, error) {
151 log.Debugw("loading-device", log.Fields{"deviceId": deviceId})
152 // Sanity check
153 if deviceId == "" {
154 return nil, status.Error(codes.InvalidArgument, "deviceId empty")
155 }
156 if !dMgr.IsDeviceInCache(deviceId) {
157 agent := newDeviceAgent(&voltha.Device{Id: deviceId}, dMgr, dMgr.clusterDataProxy)
158 if err := agent.start(nil, true); err != nil {
159 agent.stop(nil)
160 return nil, err
161 }
162 dMgr.addDeviceAgentToMap(agent)
163 }
164 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
165 return agent, nil
166 }
Kent Hagerman0ab4cb22019-04-24 13:13:35 -0400167 return nil, status.Error(codes.NotFound, deviceId) // This should nto happen
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500168}
169
170// loadRootDeviceParentAndChildren loads the children and parents of a root device in memory
171func (dMgr *DeviceManager) loadRootDeviceParentAndChildren(device *voltha.Device) error {
172 log.Debugw("loading-parent-and-children", log.Fields{"deviceId": device.Id})
173 if device.Root {
174 // Scenario A
175 if device.ParentId != "" {
176 // Load logical device if needed.
177 if err := dMgr.logicalDeviceMgr.load(device.ParentId); err != nil {
178 log.Warnw("failure-loading-logical-device", log.Fields{"lDeviceId": device.ParentId})
179 }
180 } else {
181 log.Debugw("no-parent-to-load", log.Fields{"deviceId": device.Id})
182 }
183 // Load all child devices, if needed
184 if childDeviceIds, err := dMgr.getAllChildDeviceIds(device); err == nil {
185 for _, childDeviceId := range childDeviceIds {
186 if _, err := dMgr.loadDevice(childDeviceId); err != nil {
187 log.Warnw("failure-loading-device", log.Fields{"deviceId": childDeviceId})
188 return err
189 }
190 }
191 log.Debugw("loaded-children", log.Fields{"deviceId": device.Id, "numChildren": len(childDeviceIds)})
192 } else {
193 log.Debugw("no-child-to-load", log.Fields{"deviceId": device.Id})
194 }
195 }
196 return nil
197}
198
199// load loads the deviceId in memory, if not present, and also loads its accompanying parents and children. Loading
200// in memory is for improved performance. It is not imperative that a device needs to be in memory when a request
201// acting on the device is received by the core. In such a scenario, the Core will load the device in memory first
202// and the proceed with the request.
203func (dMgr *DeviceManager) load(deviceId string) error {
204 log.Debug("load...")
205 // First load the device - this may fail in case the device was deleted intentionally by the other core
206 var dAgent *DeviceAgent
207 var err error
208 if dAgent, err = dMgr.loadDevice(deviceId); err != nil {
209 log.Warnw("failure-loading-device", log.Fields{"deviceId": deviceId})
210 return err
211 }
212 // Get the loaded device details
213 var device *voltha.Device
214 if device, err = dAgent.getDevice(); err != nil {
215 return err
216 }
217
218 // If the device is in Pre-provisioning or deleted state stop here
219 if device.AdminState == voltha.AdminState_PREPROVISIONED || device.AdminState == voltha.AdminState_DELETED {
220 return nil
221 }
222
223 // Now we face two scenarios
224 if device.Root {
225 // Load all children as well as the parent of this device (logical_device)
226 if err := dMgr.loadRootDeviceParentAndChildren(device); err != nil {
227 log.Warnw("failure-loading-device-parent-and-children", log.Fields{"deviceId": deviceId})
228 return err
229 }
230 log.Debugw("successfully-loaded-parent-and-children", log.Fields{"deviceId": deviceId})
231 } else {
232 // Scenario B - use the parentId of that device (root device) to trigger the loading
233 if device.ParentId != "" {
234 return dMgr.load(device.ParentId)
235 }
236 }
237 return nil
238}
239
Stephane Barbariea75791c2019-01-24 10:58:06 -0500240// ListDeviceIds retrieves the latest device IDs information from the data model (memory data only)
241func (dMgr *DeviceManager) ListDeviceIds() (*voltha.IDs, error) {
242 log.Debug("ListDeviceIDs")
243 // Report only device IDs that are in the device agent map
244 return dMgr.listDeviceIdsFromMap(), nil
245}
246
247//ReconcileDevices is a request to a voltha core to managed a list of devices based on their IDs
248func (dMgr *DeviceManager) ReconcileDevices(ctx context.Context, ids *voltha.IDs, ch chan interface{}) {
249 log.Debug("ReconcileDevices")
250 var res interface{}
251 if ids != nil {
252 toReconcile := len(ids.Items)
253 reconciled := 0
254 for _, id := range ids.Items {
255 // Act on the device only if its not present in the agent map
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500256 if !dMgr.IsDeviceInCache(id.Id) {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500257 // Device Id not in memory
258 log.Debugw("reconciling-device", log.Fields{"id": id.Id})
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500259 // Load device from dB
260 agent := newDeviceAgent(&voltha.Device{Id: id.Id}, dMgr, dMgr.clusterDataProxy)
261 if err := agent.start(nil, true); err != nil {
262 log.Warnw("failure-loading-device", log.Fields{"deviceId": id.Id})
263 agent.stop(nil)
Stephane Barbariea75791c2019-01-24 10:58:06 -0500264 } else {
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500265 dMgr.addDeviceAgentToMap(agent)
266 reconciled += 1
Stephane Barbariea75791c2019-01-24 10:58:06 -0500267 }
268 } else {
269 reconciled += 1
270 }
271 }
272 if toReconcile != reconciled {
273 res = status.Errorf(codes.DataLoss, "less-device-reconciled:%d/%d", reconciled, toReconcile)
274 }
275 } else {
276 res = status.Errorf(codes.InvalidArgument, "empty-list-of-ids")
277 }
278 sendResponse(ctx, ch, res)
279}
280
281func (dMgr *DeviceManager) getPorts(ctx context.Context, deviceId string, portType voltha.Port_PortType) (*voltha.Ports, error) {
282 log.Debugw("getPorts", log.Fields{"deviceid": deviceId, "portType": portType})
283 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
284 return agent.getPorts(ctx, portType), nil
285 }
286 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
287
288}
289
290func (dMgr *DeviceManager) ListDevicePorts(ctx context.Context, deviceId string) (*voltha.Ports, error) {
291 log.Debugw("ListDevicePorts", log.Fields{"deviceid": deviceId})
292 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
293 return agent.ListDevicePorts(ctx)
294 }
295 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
296
297}
298
299func (dMgr *DeviceManager) ListDevicePmConfigs(ctx context.Context, deviceId string) (*voltha.PmConfigs, error) {
300 log.Debugw("ListDevicePmConfigs", log.Fields{"deviceid": deviceId})
301 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
302 return agent.ListDevicePmConfigs(ctx)
303 }
304 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
305
306}
307
308func (dMgr *DeviceManager) ListDeviceFlows(ctx context.Context, deviceId string) (*voltha.Flows, error) {
309 log.Debugw("ListDeviceFlows", log.Fields{"deviceid": deviceId})
310 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
311 return agent.ListDeviceFlows(ctx)
312 }
313 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
Stephane Barbariea75791c2019-01-24 10:58:06 -0500314}
315
316func (dMgr *DeviceManager) ListDeviceFlowGroups(ctx context.Context, deviceId string) (*voltha.FlowGroups, error) {
317 log.Debugw("ListDeviceFlowGroups", log.Fields{"deviceid": deviceId})
318 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
319 return agent.ListDeviceFlowGroups(ctx)
320 }
321 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
322
323}
324
325func (dMgr *DeviceManager) GetImageDownloadStatus(ctx context.Context, deviceId string, imageName string) (*voltha.ImageDownload, error) {
326 log.Debugw("GetImageDownloadStatus", log.Fields{"deviceid": deviceId, "imagename": imageName})
327 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
328 return agent.GetImageDownloadStatus(ctx, imageName)
329 }
330 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
331
332}
333
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500334func (dMgr *DeviceManager) GetImageDownload(ctx context.Context, deviceId string, imageName string) (*voltha.ImageDownload, error) {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500335 log.Debugw("GetImageDownload", log.Fields{"deviceid": deviceId, "imagename": imageName})
336 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
337 return agent.GetImageDownload(ctx, imageName)
338 }
339 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
340
341}
342
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500343func (dMgr *DeviceManager) ListImageDownloads(ctx context.Context, deviceId string) (*voltha.ImageDownloads, error) {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500344 log.Debugw("ListImageDownloads", log.Fields{"deviceid": deviceId})
345 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
346 return agent.ListImageDownloads(ctx)
347 }
348 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
349
350}
351
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500352func (dMgr *DeviceManager) GetImages(ctx context.Context, deviceId string) (*voltha.Images, error) {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500353 log.Debugw("GetImages", log.Fields{"deviceid": deviceId})
354 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
355 return agent.GetImages(ctx)
356 }
357 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
358
359}
360
361func (dMgr *DeviceManager) getParentDevice(childDevice *voltha.Device) *voltha.Device {
362 // Sanity check
363 if childDevice.Root {
364 // childDevice is the parent device
365 return childDevice
366 }
367 parentDevice, _ := dMgr.GetDevice(childDevice.ParentId)
368 return parentDevice
369}
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500370
371//getAllChildDeviceIds is a helper method to get all the child device IDs from the device passed as parameter
372func (dMgr *DeviceManager) getAllChildDeviceIds(parentDevice *voltha.Device) ([]string, error) {
373 log.Debugw("getAllChildDeviceIds", log.Fields{"parentDeviceId": parentDevice.Id})
374 childDeviceIds := make([]string, 0)
375 if parentDevice != nil {
376 for _, port := range parentDevice.Ports {
377 for _, peer := range port.Peers {
378 childDeviceIds = append(childDeviceIds, peer.DeviceId)
379 }
380 }
381 }
382 return childDeviceIds, nil
383}