blob: 1851e27de052575fb82a211d35dacd2d07743fe2 [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"
22 "github.com/opencord/voltha-go/protos/voltha"
23 "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 {
86 // TODO If the device is not in memory it needs to be loaded first
87 dMgr.lockDeviceAgentsMap.Lock()
88 defer dMgr.lockDeviceAgentsMap.Unlock()
89 if agent, ok := dMgr.deviceAgents[deviceId]; ok {
90 return agent
91 }
92 return nil
93}
94
95func (dMgr *DeviceManager) listDeviceIdsFromMap() *voltha.IDs {
96 dMgr.lockDeviceAgentsMap.Lock()
97 defer dMgr.lockDeviceAgentsMap.Unlock()
98 result := &voltha.IDs{Items: make([]*voltha.ID, 0)}
99 for key, _ := range dMgr.deviceAgents {
100 result.Items = append(result.Items, &voltha.ID{Id: key})
101 }
102 return result
103}
104
105func (dMgr *DeviceManager) GetDevice(id string) (*voltha.Device, error) {
106 log.Debugw("GetDevice", log.Fields{"deviceid": id})
107 if agent := dMgr.getDeviceAgent(id); agent != nil {
108 return agent.getDevice()
109 }
110 return nil, status.Errorf(codes.NotFound, "%s", id)
111}
112
113func (dMgr *DeviceManager) IsRootDevice(id string) (bool, error) {
114 device, err := dMgr.GetDevice(id)
115 if err != nil {
116 return false, err
117 }
118 return device.Root, nil
119}
120
121// GetDevice retrieves the latest device information from the data model
122func (dMgr *DeviceManager) ListDevices() (*voltha.Devices, error) {
123 log.Debug("ListDevices")
124 result := &voltha.Devices{}
125 if devices := dMgr.clusterDataProxy.Get("/devices", 0, false, ""); devices != nil {
126 for _, device := range devices.([]interface{}) {
127 if agent := dMgr.getDeviceAgent(device.(*voltha.Device).Id); agent == nil {
128 agent = newDeviceAgent(device.(*voltha.Device), dMgr, dMgr.clusterDataProxy)
129 dMgr.addDeviceAgentToMap(agent)
130 agent.start(nil)
131 }
132 result.Items = append(result.Items, device.(*voltha.Device))
133 }
134 }
135 return result, nil
136}
137
138// ListDeviceIds retrieves the latest device IDs information from the data model (memory data only)
139func (dMgr *DeviceManager) ListDeviceIds() (*voltha.IDs, error) {
140 log.Debug("ListDeviceIDs")
141 // Report only device IDs that are in the device agent map
142 return dMgr.listDeviceIdsFromMap(), nil
143}
144
145//ReconcileDevices is a request to a voltha core to managed a list of devices based on their IDs
146func (dMgr *DeviceManager) ReconcileDevices(ctx context.Context, ids *voltha.IDs, ch chan interface{}) {
147 log.Debug("ReconcileDevices")
148 var res interface{}
149 if ids != nil {
150 toReconcile := len(ids.Items)
151 reconciled := 0
152 for _, id := range ids.Items {
153 // Act on the device only if its not present in the agent map
154 if agent := dMgr.getDeviceAgent(id.Id); agent == nil {
155 // Device Id not in memory
156 log.Debugw("reconciling-device", log.Fields{"id": id.Id})
157 // Load device from model
158 if device := dMgr.clusterDataProxy.Get("/devices/"+id.Id, 0, false, ""); device != nil {
159 agent = newDeviceAgent(device.(*voltha.Device), dMgr, dMgr.clusterDataProxy)
160 dMgr.addDeviceAgentToMap(agent)
161 agent.start(nil)
162 reconciled += 1
163 } else {
164 log.Warnw("device-inexistent", log.Fields{"id": id.Id})
165 }
166 } else {
167 reconciled += 1
168 }
169 }
170 if toReconcile != reconciled {
171 res = status.Errorf(codes.DataLoss, "less-device-reconciled:%d/%d", reconciled, toReconcile)
172 }
173 } else {
174 res = status.Errorf(codes.InvalidArgument, "empty-list-of-ids")
175 }
176 sendResponse(ctx, ch, res)
177}
178
179func (dMgr *DeviceManager) getPorts(ctx context.Context, deviceId string, portType voltha.Port_PortType) (*voltha.Ports, error) {
180 log.Debugw("getPorts", log.Fields{"deviceid": deviceId, "portType": portType})
181 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
182 return agent.getPorts(ctx, portType), nil
183 }
184 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
185
186}
187
188func (dMgr *DeviceManager) ListDevicePorts(ctx context.Context, deviceId string) (*voltha.Ports, error) {
189 log.Debugw("ListDevicePorts", log.Fields{"deviceid": deviceId})
190 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
191 return agent.ListDevicePorts(ctx)
192 }
193 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
194
195}
196
197func (dMgr *DeviceManager) ListDevicePmConfigs(ctx context.Context, deviceId string) (*voltha.PmConfigs, error) {
198 log.Debugw("ListDevicePmConfigs", log.Fields{"deviceid": deviceId})
199 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
200 return agent.ListDevicePmConfigs(ctx)
201 }
202 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
203
204}
205
206func (dMgr *DeviceManager) ListDeviceFlows(ctx context.Context, deviceId string) (*voltha.Flows, error) {
207 log.Debugw("ListDeviceFlows", log.Fields{"deviceid": deviceId})
208 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
209 return agent.ListDeviceFlows(ctx)
210 }
211 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
212
213}
214
215func (dMgr *DeviceManager) ListDeviceFlowGroups(ctx context.Context, deviceId string) (*voltha.FlowGroups, error) {
216 log.Debugw("ListDeviceFlowGroups", log.Fields{"deviceid": deviceId})
217 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
218 return agent.ListDeviceFlowGroups(ctx)
219 }
220 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
221
222}
223
224func (dMgr *DeviceManager) GetImageDownloadStatus(ctx context.Context, deviceId string, imageName string) (*voltha.ImageDownload, error) {
225 log.Debugw("GetImageDownloadStatus", log.Fields{"deviceid": deviceId, "imagename": imageName})
226 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
227 return agent.GetImageDownloadStatus(ctx, imageName)
228 }
229 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
230
231}
232
233func (dMgr *DeviceManager) GetImageDownload(ctx context.Context, deviceId string, imageName string) ( *voltha.ImageDownload, error) {
234 log.Debugw("GetImageDownload", log.Fields{"deviceid": deviceId, "imagename": imageName})
235 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
236 return agent.GetImageDownload(ctx, imageName)
237 }
238 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
239
240}
241
242func (dMgr *DeviceManager) ListImageDownloads(ctx context.Context, deviceId string) ( *voltha.ImageDownloads, error) {
243 log.Debugw("ListImageDownloads", log.Fields{"deviceid": deviceId})
244 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
245 return agent.ListImageDownloads(ctx)
246 }
247 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
248
249}
250
251func (dMgr *DeviceManager) GetImages(ctx context.Context, deviceId string) ( *voltha.Images, error) {
252 log.Debugw("GetImages", log.Fields{"deviceid": deviceId})
253 if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
254 return agent.GetImages(ctx)
255 }
256 return nil, status.Errorf(codes.NotFound, "%s", deviceId)
257
258}
259
260func (dMgr *DeviceManager) getParentDevice(childDevice *voltha.Device) *voltha.Device {
261 // Sanity check
262 if childDevice.Root {
263 // childDevice is the parent device
264 return childDevice
265 }
266 parentDevice, _ := dMgr.GetDevice(childDevice.ParentId)
267 return parentDevice
268}