blob: 90c7822e993d4233c9ee41f2b44cbdacc9d9ca71 [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 {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040029 deviceAgents sync.Map
30 logicalDeviceMgr *LogicalDeviceManager
31 clusterDataProxy *model.Proxy
32 coreInstanceId string
33 exitChannel chan int
Stephane Barbariea75791c2019-01-24 10:58:06 -050034}
35
36func newDeviceManager(cdProxy *model.Proxy, coreInstanceId string) *DeviceManager {
37 var deviceMgr DeviceManager
38 deviceMgr.exitChannel = make(chan int, 1)
Stephane Barbariea75791c2019-01-24 10:58:06 -050039 deviceMgr.coreInstanceId = coreInstanceId
40 deviceMgr.clusterDataProxy = cdProxy
Stephane Barbariea75791c2019-01-24 10:58:06 -050041 return &deviceMgr
42}
43
44func (dMgr *DeviceManager) start(ctx context.Context, logicalDeviceMgr *LogicalDeviceManager) {
45 log.Info("starting-device-manager")
46 dMgr.logicalDeviceMgr = logicalDeviceMgr
47 log.Info("device-manager-started")
48}
49
50func (dMgr *DeviceManager) stop(ctx context.Context) {
51 log.Info("stopping-device-manager")
52 dMgr.exitChannel <- 1
53 log.Info("device-manager-stopped")
54}
55
56func sendResponse(ctx context.Context, ch chan interface{}, result interface{}) {
57 if ctx.Err() == nil {
58 // Returned response only of the ctx has not been cancelled/timeout/etc
59 // Channel is automatically closed when a context is Done
60 ch <- result
61 log.Debugw("sendResponse", log.Fields{"result": result})
62 } else {
63 // Should the transaction be reverted back?
64 log.Debugw("sendResponse-context-error", log.Fields{"context-error": ctx.Err()})
65 }
66}
67
68func (dMgr *DeviceManager) addDeviceAgentToMap(agent *DeviceAgent) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040069 if _, exist := dMgr.deviceAgents.Load(agent.deviceId); !exist {
70 dMgr.deviceAgents.Store(agent.deviceId, agent)
Stephane Barbariea75791c2019-01-24 10:58:06 -050071 }
72}
73
74func (dMgr *DeviceManager) deleteDeviceAgentToMap(agent *DeviceAgent) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040075 dMgr.deviceAgents.Delete(agent.deviceId)
Stephane Barbariea75791c2019-01-24 10:58:06 -050076}
77
78func (dMgr *DeviceManager) getDeviceAgent(deviceId string) *DeviceAgent {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040079 if agent, ok := dMgr.deviceAgents.Load(deviceId); ok {
80 return agent.(*DeviceAgent)
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -050081 } else {
82 // Try to load into memory - loading will also create the device agent
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -050083 if err := dMgr.load(deviceId); err == nil {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040084 if agent, ok = dMgr.deviceAgents.Load(deviceId); ok {
85 return agent.(*DeviceAgent)
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -050086 }
87 }
Stephane Barbariea75791c2019-01-24 10:58:06 -050088 }
89 return nil
90}
91
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -050092// listDeviceIdsFromMap returns the list of device IDs that are in memory
Stephane Barbariea75791c2019-01-24 10:58:06 -050093func (dMgr *DeviceManager) listDeviceIdsFromMap() *voltha.IDs {
Stephane Barbariea75791c2019-01-24 10:58:06 -050094 result := &voltha.IDs{Items: make([]*voltha.ID, 0)}
Stephane Barbarieef6650d2019-07-18 12:15:09 -040095 dMgr.deviceAgents.Range(func(key, value interface{}) bool {
96 result.Items = append(result.Items, &voltha.ID{Id: key.(string)})
97 return true
98 })
Stephane Barbariea75791c2019-01-24 10:58:06 -050099 return result
100}
101
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500102// GetDevice will returns a device, either from memory or from the dB, if present
Stephane Barbariea75791c2019-01-24 10:58:06 -0500103func (dMgr *DeviceManager) GetDevice(id string) (*voltha.Device, error) {
104 log.Debugw("GetDevice", log.Fields{"deviceid": id})
105 if agent := dMgr.getDeviceAgent(id); agent != nil {
106 return agent.getDevice()
107 }
108 return nil, status.Errorf(codes.NotFound, "%s", id)
109}
110
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500111func (dMgr *DeviceManager) IsDeviceInCache(id string) bool {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400112 _, exist := dMgr.deviceAgents.Load(id)
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500113 return exist
114}
115
Stephane Barbariea75791c2019-01-24 10:58:06 -0500116func (dMgr *DeviceManager) IsRootDevice(id string) (bool, error) {
117 device, err := dMgr.GetDevice(id)
118 if err != nil {
119 return false, err
120 }
121 return device.Root, nil
122}
123
Stephane Barbarieaa467942019-02-06 14:09:44 -0500124// ListDevices retrieves the latest devices from the data model
Stephane Barbariea75791c2019-01-24 10:58:06 -0500125func (dMgr *DeviceManager) ListDevices() (*voltha.Devices, error) {
126 log.Debug("ListDevices")
127 result := &voltha.Devices{}
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400128 if devices := dMgr.clusterDataProxy.List(context.Background(), "/devices", 0, false, ""); devices != nil {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500129 for _, device := range devices.([]interface{}) {
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500130 // If device is not in memory then set it up
131 if !dMgr.IsDeviceInCache(device.(*voltha.Device).Id) {
132 agent := newDeviceAgent(device.(*voltha.Device), dMgr, dMgr.clusterDataProxy)
133 if err := agent.start(nil, true); err != nil {
134 log.Warnw("failure-starting-agent", log.Fields{"deviceId": device.(*voltha.Device).Id})
135 agent.stop(nil)
136 } else {
137 dMgr.addDeviceAgentToMap(agent)
138 }
Stephane Barbariea75791c2019-01-24 10:58:06 -0500139 }
140 result.Items = append(result.Items, device.(*voltha.Device))
141 }
142 }
143 return result, nil
144}
145
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500146// loadDevice loads the deviceId in memory, if not present
147func (dMgr *DeviceManager) loadDevice(deviceId string) (*DeviceAgent, error) {
148 log.Debugw("loading-device", log.Fields{"deviceId": deviceId})
149 // Sanity check
150 if deviceId == "" {
151 return nil, status.Error(codes.InvalidArgument, "deviceId empty")
152 }
153 if !dMgr.IsDeviceInCache(deviceId) {
154 agent := newDeviceAgent(&voltha.Device{Id: deviceId}, dMgr, dMgr.clusterDataProxy)
155 if err := agent.start(nil, true); err != nil {
156 agent.stop(nil)
157 return nil, err
158 }
159 dMgr.addDeviceAgentToMap(agent)
160 }
161 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
162 return agent, nil
163 }
Kent Hagerman0ab4cb22019-04-24 13:13:35 -0400164 return nil, status.Error(codes.NotFound, deviceId) // This should nto happen
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500165}
166
167// loadRootDeviceParentAndChildren loads the children and parents of a root device in memory
168func (dMgr *DeviceManager) loadRootDeviceParentAndChildren(device *voltha.Device) error {
169 log.Debugw("loading-parent-and-children", log.Fields{"deviceId": device.Id})
170 if device.Root {
171 // Scenario A
172 if device.ParentId != "" {
173 // Load logical device if needed.
174 if err := dMgr.logicalDeviceMgr.load(device.ParentId); err != nil {
175 log.Warnw("failure-loading-logical-device", log.Fields{"lDeviceId": device.ParentId})
176 }
177 } else {
178 log.Debugw("no-parent-to-load", log.Fields{"deviceId": device.Id})
179 }
180 // Load all child devices, if needed
181 if childDeviceIds, err := dMgr.getAllChildDeviceIds(device); err == nil {
182 for _, childDeviceId := range childDeviceIds {
183 if _, err := dMgr.loadDevice(childDeviceId); err != nil {
184 log.Warnw("failure-loading-device", log.Fields{"deviceId": childDeviceId})
185 return err
186 }
187 }
188 log.Debugw("loaded-children", log.Fields{"deviceId": device.Id, "numChildren": len(childDeviceIds)})
189 } else {
190 log.Debugw("no-child-to-load", log.Fields{"deviceId": device.Id})
191 }
192 }
193 return nil
194}
195
196// load loads the deviceId in memory, if not present, and also loads its accompanying parents and children. Loading
197// in memory is for improved performance. It is not imperative that a device needs to be in memory when a request
198// acting on the device is received by the core. In such a scenario, the Core will load the device in memory first
199// and the proceed with the request.
200func (dMgr *DeviceManager) load(deviceId string) error {
201 log.Debug("load...")
202 // First load the device - this may fail in case the device was deleted intentionally by the other core
203 var dAgent *DeviceAgent
204 var err error
205 if dAgent, err = dMgr.loadDevice(deviceId); err != nil {
206 log.Warnw("failure-loading-device", log.Fields{"deviceId": deviceId})
207 return err
208 }
209 // Get the loaded device details
210 var device *voltha.Device
211 if device, err = dAgent.getDevice(); err != nil {
212 return err
213 }
214
215 // If the device is in Pre-provisioning or deleted state stop here
216 if device.AdminState == voltha.AdminState_PREPROVISIONED || device.AdminState == voltha.AdminState_DELETED {
217 return nil
218 }
219
220 // Now we face two scenarios
221 if device.Root {
222 // Load all children as well as the parent of this device (logical_device)
223 if err := dMgr.loadRootDeviceParentAndChildren(device); err != nil {
224 log.Warnw("failure-loading-device-parent-and-children", log.Fields{"deviceId": deviceId})
225 return err
226 }
227 log.Debugw("successfully-loaded-parent-and-children", log.Fields{"deviceId": deviceId})
228 } else {
229 // Scenario B - use the parentId of that device (root device) to trigger the loading
230 if device.ParentId != "" {
231 return dMgr.load(device.ParentId)
232 }
233 }
234 return nil
235}
236
Stephane Barbariea75791c2019-01-24 10:58:06 -0500237// ListDeviceIds retrieves the latest device IDs information from the data model (memory data only)
238func (dMgr *DeviceManager) ListDeviceIds() (*voltha.IDs, error) {
239 log.Debug("ListDeviceIDs")
240 // Report only device IDs that are in the device agent map
241 return dMgr.listDeviceIdsFromMap(), nil
242}
243
244//ReconcileDevices is a request to a voltha core to managed a list of devices based on their IDs
245func (dMgr *DeviceManager) ReconcileDevices(ctx context.Context, ids *voltha.IDs, ch chan interface{}) {
246 log.Debug("ReconcileDevices")
247 var res interface{}
248 if ids != nil {
249 toReconcile := len(ids.Items)
250 reconciled := 0
251 for _, id := range ids.Items {
252 // Act on the device only if its not present in the agent map
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500253 if !dMgr.IsDeviceInCache(id.Id) {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500254 // Device Id not in memory
255 log.Debugw("reconciling-device", log.Fields{"id": id.Id})
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500256 // Load device from dB
257 agent := newDeviceAgent(&voltha.Device{Id: id.Id}, dMgr, dMgr.clusterDataProxy)
258 if err := agent.start(nil, true); err != nil {
259 log.Warnw("failure-loading-device", log.Fields{"deviceId": id.Id})
260 agent.stop(nil)
Stephane Barbariea75791c2019-01-24 10:58:06 -0500261 } else {
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500262 dMgr.addDeviceAgentToMap(agent)
263 reconciled += 1
Stephane Barbariea75791c2019-01-24 10:58:06 -0500264 }
265 } else {
266 reconciled += 1
267 }
268 }
269 if toReconcile != reconciled {
270 res = status.Errorf(codes.DataLoss, "less-device-reconciled:%d/%d", reconciled, toReconcile)
271 }
272 } else {
273 res = status.Errorf(codes.InvalidArgument, "empty-list-of-ids")
274 }
275 sendResponse(ctx, ch, res)
276}
277
278func (dMgr *DeviceManager) getPorts(ctx context.Context, deviceId string, portType voltha.Port_PortType) (*voltha.Ports, error) {
279 log.Debugw("getPorts", log.Fields{"deviceid": deviceId, "portType": portType})
280 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
281 return agent.getPorts(ctx, portType), nil
282 }
283 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
284
285}
286
287func (dMgr *DeviceManager) ListDevicePorts(ctx context.Context, deviceId string) (*voltha.Ports, error) {
288 log.Debugw("ListDevicePorts", log.Fields{"deviceid": deviceId})
289 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
290 return agent.ListDevicePorts(ctx)
291 }
292 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
293
294}
295
296func (dMgr *DeviceManager) ListDevicePmConfigs(ctx context.Context, deviceId string) (*voltha.PmConfigs, error) {
297 log.Debugw("ListDevicePmConfigs", log.Fields{"deviceid": deviceId})
298 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
299 return agent.ListDevicePmConfigs(ctx)
300 }
301 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
302
303}
304
305func (dMgr *DeviceManager) ListDeviceFlows(ctx context.Context, deviceId string) (*voltha.Flows, error) {
306 log.Debugw("ListDeviceFlows", log.Fields{"deviceid": deviceId})
307 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
308 return agent.ListDeviceFlows(ctx)
309 }
310 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
Stephane Barbariea75791c2019-01-24 10:58:06 -0500311}
312
313func (dMgr *DeviceManager) ListDeviceFlowGroups(ctx context.Context, deviceId string) (*voltha.FlowGroups, error) {
314 log.Debugw("ListDeviceFlowGroups", log.Fields{"deviceid": deviceId})
315 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
316 return agent.ListDeviceFlowGroups(ctx)
317 }
318 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
319
320}
321
322func (dMgr *DeviceManager) GetImageDownloadStatus(ctx context.Context, deviceId string, imageName string) (*voltha.ImageDownload, error) {
323 log.Debugw("GetImageDownloadStatus", log.Fields{"deviceid": deviceId, "imagename": imageName})
324 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
325 return agent.GetImageDownloadStatus(ctx, imageName)
326 }
327 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
328
329}
330
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500331func (dMgr *DeviceManager) GetImageDownload(ctx context.Context, deviceId string, imageName string) (*voltha.ImageDownload, error) {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500332 log.Debugw("GetImageDownload", log.Fields{"deviceid": deviceId, "imagename": imageName})
333 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
334 return agent.GetImageDownload(ctx, imageName)
335 }
336 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
337
338}
339
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500340func (dMgr *DeviceManager) ListImageDownloads(ctx context.Context, deviceId string) (*voltha.ImageDownloads, error) {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500341 log.Debugw("ListImageDownloads", log.Fields{"deviceid": deviceId})
342 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
343 return agent.ListImageDownloads(ctx)
344 }
345 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
346
347}
348
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500349func (dMgr *DeviceManager) GetImages(ctx context.Context, deviceId string) (*voltha.Images, error) {
Stephane Barbariea75791c2019-01-24 10:58:06 -0500350 log.Debugw("GetImages", log.Fields{"deviceid": deviceId})
351 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
352 return agent.GetImages(ctx)
353 }
354 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
355
356}
357
358func (dMgr *DeviceManager) getParentDevice(childDevice *voltha.Device) *voltha.Device {
359 // Sanity check
360 if childDevice.Root {
361 // childDevice is the parent device
362 return childDevice
363 }
364 parentDevice, _ := dMgr.GetDevice(childDevice.ParentId)
365 return parentDevice
366}
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500367
368//getAllChildDeviceIds is a helper method to get all the child device IDs from the device passed as parameter
369func (dMgr *DeviceManager) getAllChildDeviceIds(parentDevice *voltha.Device) ([]string, error) {
370 log.Debugw("getAllChildDeviceIds", log.Fields{"parentDeviceId": parentDevice.Id})
371 childDeviceIds := make([]string, 0)
372 if parentDevice != nil {
373 for _, port := range parentDevice.Ports {
374 for _, peer := range port.Peers {
375 childDeviceIds = append(childDeviceIds, peer.DeviceId)
376 }
377 }
378 }
379 return childDeviceIds, nil
380}