blob: f3a1f6c1935ebd3d49f1e1d22f0b201811309294 [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"
20 "github.com/opencord/voltha-go/common/log"
21 "github.com/opencord/voltha-go/db/model"
William Kurkiandaa6bb22019-03-07 12:26:28 -050022 "github.com/opencord/voltha-protos/go/voltha"
Stephane Barbariea75791c2019-01-24 10:58:06 -050023 "google.golang.org/grpc/codes"
24 "google.golang.org/grpc/status"
25 "sync"
26)
27
28type DeviceManager struct {
29 deviceAgents map[string]*DeviceAgent
30 logicalDeviceMgr *LogicalDeviceManager
31 clusterDataProxy *model.Proxy
32 coreInstanceId string
33 exitChannel chan int
34 lockDeviceAgentsMap sync.RWMutex
35}
36
37func newDeviceManager(cdProxy *model.Proxy, coreInstanceId string) *DeviceManager {
38 var deviceMgr DeviceManager
39 deviceMgr.exitChannel = make(chan int, 1)
40 deviceMgr.deviceAgents = make(map[string]*DeviceAgent)
41 deviceMgr.coreInstanceId = coreInstanceId
42 deviceMgr.clusterDataProxy = cdProxy
43 deviceMgr.lockDeviceAgentsMap = sync.RWMutex{}
44 return &deviceMgr
45}
46
47func (dMgr *DeviceManager) start(ctx context.Context, logicalDeviceMgr *LogicalDeviceManager) {
48 log.Info("starting-device-manager")
49 dMgr.logicalDeviceMgr = logicalDeviceMgr
50 log.Info("device-manager-started")
51}
52
53func (dMgr *DeviceManager) stop(ctx context.Context) {
54 log.Info("stopping-device-manager")
55 dMgr.exitChannel <- 1
56 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) {
72 dMgr.lockDeviceAgentsMap.Lock()
73 defer dMgr.lockDeviceAgentsMap.Unlock()
74 if _, exist := dMgr.deviceAgents[agent.deviceId]; !exist {
75 dMgr.deviceAgents[agent.deviceId] = agent
76 }
77}
78
79func (dMgr *DeviceManager) deleteDeviceAgentToMap(agent *DeviceAgent) {
80 dMgr.lockDeviceAgentsMap.Lock()
81 defer dMgr.lockDeviceAgentsMap.Unlock()
82 delete(dMgr.deviceAgents, agent.deviceId)
83}
84
85func (dMgr *DeviceManager) getDeviceAgent(deviceId string) *DeviceAgent {
Stephane Barbariea75791c2019-01-24 10:58:06 -050086 dMgr.lockDeviceAgentsMap.Lock()
Stephane Barbariea75791c2019-01-24 10:58:06 -050087 if agent, ok := dMgr.deviceAgents[deviceId]; ok {
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -050088 dMgr.lockDeviceAgentsMap.Unlock()
Stephane Barbariea75791c2019-01-24 10:58:06 -050089 return agent
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -050090 } else {
91 // Try to load into memory - loading will also create the device agent
92 dMgr.lockDeviceAgentsMap.Unlock()
93 if err := dMgr.load(deviceId); err == nil {
94 dMgr.lockDeviceAgentsMap.Lock()
95 defer dMgr.lockDeviceAgentsMap.Unlock()
96 if agent, ok = dMgr.deviceAgents[deviceId]; ok {
97 return agent
98 }
99 }
Stephane Barbariea75791c2019-01-24 10:58:06 -0500100 }
101 return nil
102}
103
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500104// listDeviceIdsFromMap returns the list of device IDs that are in memory
Stephane Barbariea75791c2019-01-24 10:58:06 -0500105func (dMgr *DeviceManager) listDeviceIdsFromMap() *voltha.IDs {
106 dMgr.lockDeviceAgentsMap.Lock()
107 defer dMgr.lockDeviceAgentsMap.Unlock()
108 result := &voltha.IDs{Items: make([]*voltha.ID, 0)}
109 for key, _ := range dMgr.deviceAgents {
110 result.Items = append(result.Items, &voltha.ID{Id: key})
111 }
112 return result
113}
114
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500115// GetDevice will returns a device, either from memory or from the dB, if present
Stephane Barbariea75791c2019-01-24 10:58:06 -0500116func (dMgr *DeviceManager) GetDevice(id string) (*voltha.Device, error) {
117 log.Debugw("GetDevice", log.Fields{"deviceid": id})
118 if agent := dMgr.getDeviceAgent(id); agent != nil {
119 return agent.getDevice()
120 }
121 return nil, status.Errorf(codes.NotFound, "%s", id)
122}
123
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500124func (dMgr *DeviceManager) IsDeviceInCache(id string) bool {
125 dMgr.lockDeviceAgentsMap.Lock()
126 defer dMgr.lockDeviceAgentsMap.Unlock()
127 _, exist := dMgr.deviceAgents[id]
128 return exist
129}
130
Stephane Barbariea75791c2019-01-24 10:58:06 -0500131func (dMgr *DeviceManager) IsRootDevice(id string) (bool, error) {
132 device, err := dMgr.GetDevice(id)
133 if err != nil {
134 return false, err
135 }
136 return device.Root, nil
137}
138
Stephane Barbarieaa467942019-02-06 14:09:44 -0500139// ListDevices retrieves the latest devices from the data model
Stephane Barbariea75791c2019-01-24 10:58:06 -0500140func (dMgr *DeviceManager) ListDevices() (*voltha.Devices, error) {
141 log.Debug("ListDevices")
142 result := &voltha.Devices{}
Stephane Barbarieaa467942019-02-06 14:09:44 -0500143 if devices := dMgr.clusterDataProxy.List("/devices", 0, false, ""); devices != nil {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500144 for _, device := range devices.([]interface{}) {
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500145 // If device is not in memory then set it up
146 if !dMgr.IsDeviceInCache(device.(*voltha.Device).Id) {
147 agent := newDeviceAgent(device.(*voltha.Device), dMgr, dMgr.clusterDataProxy)
148 if err := agent.start(nil, true); err != nil {
149 log.Warnw("failure-starting-agent", log.Fields{"deviceId": device.(*voltha.Device).Id})
150 agent.stop(nil)
151 } else {
152 dMgr.addDeviceAgentToMap(agent)
153 }
Stephane Barbariea75791c2019-01-24 10:58:06 -0500154 }
155 result.Items = append(result.Items, device.(*voltha.Device))
156 }
157 }
158 return result, nil
159}
160
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500161// loadDevice loads the deviceId in memory, if not present
162func (dMgr *DeviceManager) loadDevice(deviceId string) (*DeviceAgent, error) {
163 log.Debugw("loading-device", log.Fields{"deviceId": deviceId})
164 // Sanity check
165 if deviceId == "" {
166 return nil, status.Error(codes.InvalidArgument, "deviceId empty")
167 }
168 if !dMgr.IsDeviceInCache(deviceId) {
169 agent := newDeviceAgent(&voltha.Device{Id: deviceId}, dMgr, dMgr.clusterDataProxy)
170 if err := agent.start(nil, true); err != nil {
171 agent.stop(nil)
172 return nil, err
173 }
174 dMgr.addDeviceAgentToMap(agent)
175 }
176 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
177 return agent, nil
178 }
Kent Hagerman0ab4cb22019-04-24 13:13:35 -0400179 return nil, status.Error(codes.NotFound, deviceId) // This should nto happen
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500180}
181
182// loadRootDeviceParentAndChildren loads the children and parents of a root device in memory
183func (dMgr *DeviceManager) loadRootDeviceParentAndChildren(device *voltha.Device) error {
184 log.Debugw("loading-parent-and-children", log.Fields{"deviceId": device.Id})
185 if device.Root {
186 // Scenario A
187 if device.ParentId != "" {
188 // Load logical device if needed.
189 if err := dMgr.logicalDeviceMgr.load(device.ParentId); err != nil {
190 log.Warnw("failure-loading-logical-device", log.Fields{"lDeviceId": device.ParentId})
191 }
192 } else {
193 log.Debugw("no-parent-to-load", log.Fields{"deviceId": device.Id})
194 }
195 // Load all child devices, if needed
196 if childDeviceIds, err := dMgr.getAllChildDeviceIds(device); err == nil {
197 for _, childDeviceId := range childDeviceIds {
198 if _, err := dMgr.loadDevice(childDeviceId); err != nil {
199 log.Warnw("failure-loading-device", log.Fields{"deviceId": childDeviceId})
200 return err
201 }
202 }
203 log.Debugw("loaded-children", log.Fields{"deviceId": device.Id, "numChildren": len(childDeviceIds)})
204 } else {
205 log.Debugw("no-child-to-load", log.Fields{"deviceId": device.Id})
206 }
207 }
208 return nil
209}
210
211// load loads the deviceId in memory, if not present, and also loads its accompanying parents and children. Loading
212// in memory is for improved performance. It is not imperative that a device needs to be in memory when a request
213// acting on the device is received by the core. In such a scenario, the Core will load the device in memory first
214// and the proceed with the request.
215func (dMgr *DeviceManager) load(deviceId string) error {
216 log.Debug("load...")
217 // First load the device - this may fail in case the device was deleted intentionally by the other core
218 var dAgent *DeviceAgent
219 var err error
220 if dAgent, err = dMgr.loadDevice(deviceId); err != nil {
221 log.Warnw("failure-loading-device", log.Fields{"deviceId": deviceId})
222 return err
223 }
224 // Get the loaded device details
225 var device *voltha.Device
226 if device, err = dAgent.getDevice(); err != nil {
227 return err
228 }
229
230 // If the device is in Pre-provisioning or deleted state stop here
231 if device.AdminState == voltha.AdminState_PREPROVISIONED || device.AdminState == voltha.AdminState_DELETED {
232 return nil
233 }
234
235 // Now we face two scenarios
236 if device.Root {
237 // Load all children as well as the parent of this device (logical_device)
238 if err := dMgr.loadRootDeviceParentAndChildren(device); err != nil {
239 log.Warnw("failure-loading-device-parent-and-children", log.Fields{"deviceId": deviceId})
240 return err
241 }
242 log.Debugw("successfully-loaded-parent-and-children", log.Fields{"deviceId": deviceId})
243 } else {
244 // Scenario B - use the parentId of that device (root device) to trigger the loading
245 if device.ParentId != "" {
246 return dMgr.load(device.ParentId)
247 }
248 }
249 return nil
250}
251
Stephane Barbariea75791c2019-01-24 10:58:06 -0500252// ListDeviceIds retrieves the latest device IDs information from the data model (memory data only)
253func (dMgr *DeviceManager) ListDeviceIds() (*voltha.IDs, error) {
254 log.Debug("ListDeviceIDs")
255 // Report only device IDs that are in the device agent map
256 return dMgr.listDeviceIdsFromMap(), nil
257}
258
259//ReconcileDevices is a request to a voltha core to managed a list of devices based on their IDs
260func (dMgr *DeviceManager) ReconcileDevices(ctx context.Context, ids *voltha.IDs, ch chan interface{}) {
261 log.Debug("ReconcileDevices")
262 var res interface{}
263 if ids != nil {
264 toReconcile := len(ids.Items)
265 reconciled := 0
266 for _, id := range ids.Items {
267 // Act on the device only if its not present in the agent map
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500268 if !dMgr.IsDeviceInCache(id.Id) {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500269 // Device Id not in memory
270 log.Debugw("reconciling-device", log.Fields{"id": id.Id})
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500271 // Load device from dB
272 agent := newDeviceAgent(&voltha.Device{Id: id.Id}, dMgr, dMgr.clusterDataProxy)
273 if err := agent.start(nil, true); err != nil {
274 log.Warnw("failure-loading-device", log.Fields{"deviceId": id.Id})
275 agent.stop(nil)
Stephane Barbariea75791c2019-01-24 10:58:06 -0500276 } else {
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500277 dMgr.addDeviceAgentToMap(agent)
278 reconciled += 1
Stephane Barbariea75791c2019-01-24 10:58:06 -0500279 }
280 } else {
281 reconciled += 1
282 }
283 }
284 if toReconcile != reconciled {
285 res = status.Errorf(codes.DataLoss, "less-device-reconciled:%d/%d", reconciled, toReconcile)
286 }
287 } else {
288 res = status.Errorf(codes.InvalidArgument, "empty-list-of-ids")
289 }
290 sendResponse(ctx, ch, res)
291}
292
293func (dMgr *DeviceManager) getPorts(ctx context.Context, deviceId string, portType voltha.Port_PortType) (*voltha.Ports, error) {
294 log.Debugw("getPorts", log.Fields{"deviceid": deviceId, "portType": portType})
295 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
296 return agent.getPorts(ctx, portType), nil
297 }
298 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
299
300}
301
302func (dMgr *DeviceManager) ListDevicePorts(ctx context.Context, deviceId string) (*voltha.Ports, error) {
303 log.Debugw("ListDevicePorts", log.Fields{"deviceid": deviceId})
304 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
305 return agent.ListDevicePorts(ctx)
306 }
307 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
308
309}
310
311func (dMgr *DeviceManager) ListDevicePmConfigs(ctx context.Context, deviceId string) (*voltha.PmConfigs, error) {
312 log.Debugw("ListDevicePmConfigs", log.Fields{"deviceid": deviceId})
313 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
314 return agent.ListDevicePmConfigs(ctx)
315 }
316 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
317
318}
319
320func (dMgr *DeviceManager) ListDeviceFlows(ctx context.Context, deviceId string) (*voltha.Flows, error) {
321 log.Debugw("ListDeviceFlows", log.Fields{"deviceid": deviceId})
322 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
323 return agent.ListDeviceFlows(ctx)
324 }
325 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
Stephane Barbariea75791c2019-01-24 10:58:06 -0500326}
327
328func (dMgr *DeviceManager) ListDeviceFlowGroups(ctx context.Context, deviceId string) (*voltha.FlowGroups, error) {
329 log.Debugw("ListDeviceFlowGroups", log.Fields{"deviceid": deviceId})
330 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
331 return agent.ListDeviceFlowGroups(ctx)
332 }
333 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
334
335}
336
337func (dMgr *DeviceManager) GetImageDownloadStatus(ctx context.Context, deviceId string, imageName string) (*voltha.ImageDownload, error) {
338 log.Debugw("GetImageDownloadStatus", log.Fields{"deviceid": deviceId, "imagename": imageName})
339 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
340 return agent.GetImageDownloadStatus(ctx, imageName)
341 }
342 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
343
344}
345
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500346func (dMgr *DeviceManager) GetImageDownload(ctx context.Context, deviceId string, imageName string) (*voltha.ImageDownload, error) {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500347 log.Debugw("GetImageDownload", log.Fields{"deviceid": deviceId, "imagename": imageName})
348 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
349 return agent.GetImageDownload(ctx, imageName)
350 }
351 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
352
353}
354
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500355func (dMgr *DeviceManager) ListImageDownloads(ctx context.Context, deviceId string) (*voltha.ImageDownloads, error) {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500356 log.Debugw("ListImageDownloads", log.Fields{"deviceid": deviceId})
357 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
358 return agent.ListImageDownloads(ctx)
359 }
360 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
361
362}
363
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500364func (dMgr *DeviceManager) GetImages(ctx context.Context, deviceId string) (*voltha.Images, error) {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500365 log.Debugw("GetImages", log.Fields{"deviceid": deviceId})
366 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
367 return agent.GetImages(ctx)
368 }
369 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
370
371}
372
373func (dMgr *DeviceManager) getParentDevice(childDevice *voltha.Device) *voltha.Device {
374 // Sanity check
375 if childDevice.Root {
376 // childDevice is the parent device
377 return childDevice
378 }
379 parentDevice, _ := dMgr.GetDevice(childDevice.ParentId)
380 return parentDevice
381}
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500382
383//getAllChildDeviceIds is a helper method to get all the child device IDs from the device passed as parameter
384func (dMgr *DeviceManager) getAllChildDeviceIds(parentDevice *voltha.Device) ([]string, error) {
385 log.Debugw("getAllChildDeviceIds", log.Fields{"parentDeviceId": parentDevice.Id})
386 childDeviceIds := make([]string, 0)
387 if parentDevice != nil {
388 for _, port := range parentDevice.Ports {
389 for _, peer := range port.Peers {
390 childDeviceIds = append(childDeviceIds, peer.DeviceId)
391 }
392 }
393 }
394 return childDeviceIds, nil
395}