blob: 519a0a17d6c0d51f359592b88f52a81e8d9cab42 [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 "github.com/gogo/protobuf/proto"
21 "github.com/opencord/voltha-go/common/log"
22 "github.com/opencord/voltha-go/db/model"
23 ca "github.com/opencord/voltha-go/protos/core_adapter"
khenaidoo89b0e942018-10-21 21:11:33 -040024 ofp "github.com/opencord/voltha-go/protos/openflow_13"
khenaidoob9203542018-09-17 22:56:37 -040025 "github.com/opencord/voltha-go/protos/voltha"
khenaidoo89b0e942018-10-21 21:11:33 -040026 fd "github.com/opencord/voltha-go/rw_core/flow_decomposition"
27 "github.com/opencord/voltha-go/rw_core/graph"
28 fu "github.com/opencord/voltha-go/rw_core/utils"
khenaidoob9203542018-09-17 22:56:37 -040029 "google.golang.org/grpc/codes"
30 "google.golang.org/grpc/status"
khenaidoo92e62c52018-10-03 14:02:54 -040031 "sync"
khenaidoob9203542018-09-17 22:56:37 -040032)
33
34type LogicalDeviceAgent struct {
khenaidoo92e62c52018-10-03 14:02:54 -040035 logicalDeviceId string
36 lastData *voltha.LogicalDevice
37 rootDeviceId string
38 deviceMgr *DeviceManager
39 ldeviceMgr *LogicalDeviceManager
40 clusterDataProxy *model.Proxy
41 exitChannel chan int
khenaidoo89b0e942018-10-21 21:11:33 -040042 deviceGraph *graph.DeviceGraph
43 DefaultFlowRules *fu.DeviceRules
khenaidoo92e62c52018-10-03 14:02:54 -040044 lockLogicalDevice sync.RWMutex
khenaidoob9203542018-09-17 22:56:37 -040045}
46
khenaidoo4d4802d2018-10-04 21:59:49 -040047func newLogicalDeviceAgent(id string, device *voltha.Device, ldeviceMgr *LogicalDeviceManager, deviceMgr *DeviceManager,
khenaidoo9a468962018-09-19 15:33:13 -040048 cdProxy *model.Proxy) *LogicalDeviceAgent {
khenaidoob9203542018-09-17 22:56:37 -040049 var agent LogicalDeviceAgent
50 agent.exitChannel = make(chan int, 1)
51 agent.logicalDeviceId = id
52 agent.rootDeviceId = device.Id
53 agent.deviceMgr = deviceMgr
khenaidoo9a468962018-09-19 15:33:13 -040054 agent.clusterDataProxy = cdProxy
khenaidoob9203542018-09-17 22:56:37 -040055 agent.ldeviceMgr = ldeviceMgr
khenaidoo89b0e942018-10-21 21:11:33 -040056 //agent.deviceGraph =
khenaidoo92e62c52018-10-03 14:02:54 -040057 agent.lockLogicalDevice = sync.RWMutex{}
khenaidoob9203542018-09-17 22:56:37 -040058 return &agent
59}
60
khenaidoo4d4802d2018-10-04 21:59:49 -040061// start creates the logical device and add it to the data model
62func (agent *LogicalDeviceAgent) start(ctx context.Context) error {
khenaidoo92e62c52018-10-03 14:02:54 -040063 log.Infow("starting-logical_device-agent", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
khenaidoob9203542018-09-17 22:56:37 -040064 //Build the logical device based on information retrieved from the device adapter
65 var switchCap *ca.SwitchCapability
66 var err error
67 if switchCap, err = agent.deviceMgr.getSwitchCapability(ctx, agent.rootDeviceId); err != nil {
68 log.Errorw("error-creating-logical-device", log.Fields{"error": err})
69 return err
70 }
71 ld := &voltha.LogicalDevice{Id: agent.logicalDeviceId, RootDeviceId: agent.rootDeviceId}
khenaidoo89b0e942018-10-21 21:11:33 -040072 ld.Desc = (proto.Clone(switchCap.Desc)).(*ofp.OfpDesc)
73 ld.SwitchFeatures = (proto.Clone(switchCap.SwitchFeatures)).(*ofp.OfpSwitchFeatures)
khenaidoob9203542018-09-17 22:56:37 -040074
75 //Add logical ports to the logical device based on the number of NNI ports discovered
76 //First get the default port capability - TODO: each NNI port may have different capabilities,
77 //hence. may need to extract the port by the NNI port id defined by the adapter during device
78 //creation
79 var nniPorts *voltha.Ports
khenaidoo92e62c52018-10-03 14:02:54 -040080 if nniPorts, err = agent.deviceMgr.getPorts(ctx, agent.rootDeviceId, voltha.Port_ETHERNET_NNI); err != nil {
khenaidoob9203542018-09-17 22:56:37 -040081 log.Errorw("error-creating-logical-port", log.Fields{"error": err})
82 }
83 var portCap *ca.PortCapability
84 for _, port := range nniPorts.Items {
85 log.Infow("NNI PORTS", log.Fields{"NNI": port})
86 if portCap, err = agent.deviceMgr.getPortCapability(ctx, agent.rootDeviceId, port.PortNo); err != nil {
87 log.Errorw("error-creating-logical-device", log.Fields{"error": err})
88 return err
89 }
90
91 lp := (proto.Clone(portCap.Port)).(*voltha.LogicalPort)
khenaidoo92e62c52018-10-03 14:02:54 -040092 lp.DeviceId = agent.rootDeviceId
khenaidoob9203542018-09-17 22:56:37 -040093 ld.Ports = append(ld.Ports, lp)
94 }
khenaidoo92e62c52018-10-03 14:02:54 -040095 agent.lockLogicalDevice.Lock()
96 defer agent.lockLogicalDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -040097 // Save the logical device
khenaidoo9a468962018-09-19 15:33:13 -040098 if added := agent.clusterDataProxy.Add("/logical_devices", ld, ""); added == nil {
khenaidoob9203542018-09-17 22:56:37 -040099 log.Errorw("failed-to-add-logical-device", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
100 } else {
101 log.Debugw("logicaldevice-created", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
102 }
103
104 return nil
105}
106
khenaidoo4d4802d2018-10-04 21:59:49 -0400107// stop stops the logical devuce agent. This removes the logical device from the data model.
108func (agent *LogicalDeviceAgent) stop(ctx context.Context) {
109 log.Info("stopping-logical_device-agent")
110 agent.lockLogicalDevice.Lock()
111 defer agent.lockLogicalDevice.Unlock()
112 //Remove the logical device from the model
113 if removed := agent.clusterDataProxy.Remove("/logical_devices/"+agent.logicalDeviceId, ""); removed == nil {
114 log.Errorw("failed-to-remove-logical-device", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
115 } else {
116 log.Debugw("logicaldevice-removed", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
117 }
118 agent.exitChannel <- 1
119 log.Info("logical_device-agent-stopped")
120}
121
122// getLogicalDevice locks the logical device model and then retrieves the latest logical device information
khenaidoo92e62c52018-10-03 14:02:54 -0400123func (agent *LogicalDeviceAgent) getLogicalDevice() (*voltha.LogicalDevice, error) {
124 log.Debug("getLogicalDevice")
125 agent.lockLogicalDevice.Lock()
126 defer agent.lockLogicalDevice.Unlock()
127 logicalDevice := agent.clusterDataProxy.Get("/logical_devices/"+agent.logicalDeviceId, 1, false, "")
128 if lDevice, ok := logicalDevice.(*voltha.LogicalDevice); ok {
129 cloned := proto.Clone(lDevice).(*voltha.LogicalDevice)
130 return cloned, nil
131 }
132 return nil, status.Errorf(codes.NotFound, "logical_device-%s", agent.logicalDeviceId)
133}
134
khenaidoo4d4802d2018-10-04 21:59:49 -0400135// getLogicalDeviceWithoutLock retrieves a logical device from the model without locking it. This is used only by
136// functions that have already acquired the logical device lock to the model
khenaidoo92e62c52018-10-03 14:02:54 -0400137func (agent *LogicalDeviceAgent) getLogicalDeviceWithoutLock() (*voltha.LogicalDevice, error) {
138 log.Debug("getLogicalDeviceWithoutLock")
139 logicalDevice := agent.clusterDataProxy.Get("/logical_devices/"+agent.logicalDeviceId, 1, false, "")
140 if lDevice, ok := logicalDevice.(*voltha.LogicalDevice); ok {
141 cloned := proto.Clone(lDevice).(*voltha.LogicalDevice)
142 return cloned, nil
143 }
144 return nil, status.Errorf(codes.NotFound, "logical_device-%s", agent.logicalDeviceId)
145}
146
khenaidoo4d4802d2018-10-04 21:59:49 -0400147// addUNILogicalPort creates a UNI port on the logical device that represents a child device
khenaidoob9203542018-09-17 22:56:37 -0400148func (agent *LogicalDeviceAgent) addUNILogicalPort(ctx context.Context, childDevice *voltha.Device, portNo uint32) error {
khenaidoo92e62c52018-10-03 14:02:54 -0400149 log.Infow("addUNILogicalPort-start", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
khenaidoob9203542018-09-17 22:56:37 -0400150 // Build the logical device based on information retrieved from the device adapter
151 var portCap *ca.PortCapability
152 var err error
153 if portCap, err = agent.deviceMgr.getPortCapability(ctx, childDevice.Id, portNo); err != nil {
154 log.Errorw("error-creating-logical-port", log.Fields{"error": err})
155 return err
156 }
khenaidoo92e62c52018-10-03 14:02:54 -0400157 agent.lockLogicalDevice.Lock()
158 defer agent.lockLogicalDevice.Unlock()
khenaidoob9203542018-09-17 22:56:37 -0400159 // Get stored logical device
khenaidoo92e62c52018-10-03 14:02:54 -0400160 if ldevice, err := agent.getLogicalDeviceWithoutLock(); err != nil {
khenaidoob9203542018-09-17 22:56:37 -0400161 return status.Error(codes.NotFound, agent.logicalDeviceId)
162 } else {
khenaidoo92e62c52018-10-03 14:02:54 -0400163 cloned := proto.Clone(ldevice).(*voltha.LogicalDevice)
164 lp := proto.Clone(portCap.Port).(*voltha.LogicalPort)
165 lp.DeviceId = childDevice.Id
khenaidoob9203542018-09-17 22:56:37 -0400166 cloned.Ports = append(cloned.Ports, lp)
khenaidoo92e62c52018-10-03 14:02:54 -0400167 return agent.updateLogicalDeviceWithoutLock(cloned)
168 }
169}
170
171//updateLogicalDeviceWithoutLock updates the model with the logical device. It clones the logicaldevice before saving it
172func (agent *LogicalDeviceAgent) updateLogicalDeviceWithoutLock(logicalDevice *voltha.LogicalDevice) error {
173 cloned := proto.Clone(logicalDevice).(*voltha.LogicalDevice)
174 afterUpdate := agent.clusterDataProxy.Update("/logical_devices/"+agent.logicalDeviceId, cloned, false, "")
175 if afterUpdate == nil {
176 return status.Errorf(codes.Internal, "failed-updating-logical-device:%s", agent.logicalDeviceId)
177 }
178 return nil
179}
180
181// deleteLogicalPort removes the logical port associated with a child device
182func (agent *LogicalDeviceAgent) deleteLogicalPort(device *voltha.Device) error {
183 agent.lockLogicalDevice.Lock()
184 defer agent.lockLogicalDevice.Unlock()
185 // Get the most up to date logical device
186 var logicaldevice *voltha.LogicalDevice
187 if logicaldevice, _ = agent.getLogicalDeviceWithoutLock(); logicaldevice == nil {
188 log.Debugw("no-logical-device", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "deviceId": device.Id})
khenaidoob9203542018-09-17 22:56:37 -0400189 return nil
190 }
khenaidoo92e62c52018-10-03 14:02:54 -0400191 index := -1
192 for i, logicalPort := range logicaldevice.Ports {
193 if logicalPort.DeviceId == device.Id {
194 index = i
195 break
196 }
197 }
198 if index >= 0 {
199 copy(logicaldevice.Ports[index:], logicaldevice.Ports[index+1:])
200 logicaldevice.Ports[len(logicaldevice.Ports)-1] = nil
201 logicaldevice.Ports = logicaldevice.Ports[:len(logicaldevice.Ports)-1]
202 log.Debugw("logical-port-deleted", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
203 return agent.updateLogicalDeviceWithoutLock(logicaldevice)
204 }
205 return nil
khenaidoob9203542018-09-17 22:56:37 -0400206}
207
khenaidoo89b0e942018-10-21 21:11:33 -0400208func isNNIPort(portNo uint32, nniPortsNo []uint32) bool {
209 for _, pNo := range nniPortsNo {
210 if pNo == portNo {
211 return true
212 }
213 }
214 return false
215}
khenaidoo4d4802d2018-10-04 21:59:49 -0400216
khenaidoo89b0e942018-10-21 21:11:33 -0400217func (agent *LogicalDeviceAgent) getPreCalculatedRoute(ingress, egress uint32) []graph.RouteHop {
218 for routeLink, route := range agent.deviceGraph.Routes {
219 if ingress == routeLink.Ingress && egress == routeLink.Egress {
220 return route
221 }
222 }
223 log.Warnw("no-route", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "ingress": ingress, "egress": egress})
224 return nil
225}
226
227func (agent *LogicalDeviceAgent) GetRoute(ingressPortNo *uint32, egressPortNo *uint32) []graph.RouteHop {
228 agent.lockLogicalDevice.Lock()
229 defer agent.lockLogicalDevice.Unlock()
230 log.Debugw("getting-route", log.Fields{"ingress-port": ingressPortNo, "egress-port": egressPortNo})
231 // Get the updated logical device
232 var ld *ca.LogicalDevice
233 routes := make([]graph.RouteHop, 0)
234 var err error
235 if ld, err = agent.getLogicalDeviceWithoutLock(); err != nil {
236 return nil
237 }
238 nniLogicalPortsNo := make([]uint32, 0)
239 for _, logicalPort := range ld.Ports {
240 if logicalPort.RootPort {
241 nniLogicalPortsNo = append(nniLogicalPortsNo, logicalPort.OfpPort.PortNo)
242 }
243 }
244 if len(nniLogicalPortsNo) == 0 {
245 log.Errorw("no-nni-ports", log.Fields{"LogicalDeviceId": ld.Id})
246 return nil
247 }
248 // Consider different possibilities
249 if egressPortNo != nil && ((*egressPortNo & 0x7fffffff) == uint32(ofp.OfpPortNo_OFPP_CONTROLLER)) {
250 log.Debugw("controller-flow", log.Fields{"ingressPortNo": ingressPortNo, "egressPortNo": egressPortNo, "nniPortsNo": nniLogicalPortsNo})
251 if isNNIPort(*ingressPortNo, nniLogicalPortsNo) {
252 log.Debug("returning-half-route")
253 //This is a trap on the NNI Port
254 //Return a 'half' route to make the flow decomposer logic happy
255 for routeLink, route := range agent.deviceGraph.Routes {
256 if isNNIPort(routeLink.Egress, nniLogicalPortsNo) {
257 routes = append(routes, graph.RouteHop{}) // first hop is set to empty
258 routes = append(routes, route[1])
259 return routes
260 }
261 }
262 log.Warnw("no-upstream-route", log.Fields{"ingressPortNo": ingressPortNo, "egressPortNo": egressPortNo, "nniPortsNo": nniLogicalPortsNo})
263 return nil
264 }
265 //treat it as if the output port is the first NNI of the OLT
266 egressPortNo = &nniLogicalPortsNo[0]
267 }
268 //If ingress port is not specified (nil), it may be a wildcarded
269 //route if egress port is OFPP_CONTROLLER or a nni logical port,
270 //in which case we need to create a half-route where only the egress
271 //hop is filled, the first hop is nil
272 if ingressPortNo == nil && isNNIPort(*egressPortNo, nniLogicalPortsNo) {
273 // We can use the 2nd hop of any upstream route, so just find the first upstream:
274 for routeLink, route := range agent.deviceGraph.Routes {
275 if isNNIPort(routeLink.Egress, nniLogicalPortsNo) {
276 routes = append(routes, graph.RouteHop{}) // first hop is set to empty
277 routes = append(routes, route[1])
278 return routes
279 }
280 }
281 log.Warnw("no-upstream-route", log.Fields{"ingressPortNo": ingressPortNo, "egressPortNo": egressPortNo, "nniPortsNo": nniLogicalPortsNo})
282 return nil
283 }
284 //If egress port is not specified (nil), we can also can return a "half" route
285 if egressPortNo == nil {
286 for routeLink, route := range agent.deviceGraph.Routes {
287 if routeLink.Ingress == *ingressPortNo {
288 routes = append(routes, route[0])
289 routes = append(routes, graph.RouteHop{})
290 return routes
291 }
292 }
293 log.Warnw("no-downstream-route", log.Fields{"ingressPortNo": ingressPortNo, "egressPortNo": egressPortNo, "nniPortsNo": nniLogicalPortsNo})
294 return nil
295 }
296
297 // Return the pre-calculated route
298 return agent.getPreCalculatedRoute(*ingressPortNo, *egressPortNo)
299}
300
301// updateRoutes updates the device routes whenever there is a device or port changes relevant to this
302// logical device. TODO: Add more heuristics to this process to update the routes where a change has occurred
303// instead of rebuilding the entire set of routes
304func (agent *LogicalDeviceAgent) updateRoutes() {
305 if ld, err := agent.getLogicalDevice(); err == nil {
306 agent.deviceGraph.ComputeRoutes(ld.Ports)
307 }
308}
309
310func (agent *LogicalDeviceAgent) rootDeviceDefaultRules() *fu.FlowsAndGroups {
311 return fu.NewFlowsAndGroups()
312}
313
314func (agent *LogicalDeviceAgent) leafDeviceDefaultRules(deviceId string) *fu.FlowsAndGroups {
315 fg := fu.NewFlowsAndGroups()
316 var device *voltha.Device
317 var err error
318 if device, err = agent.deviceMgr.getDevice(deviceId); err != nil {
319 return fg
320 }
321 //set the upstream and downstream ports
322 upstreamPorts := make([]*voltha.Port, 0)
323 downstreamPorts := make([]*voltha.Port, 0)
324 for _, port := range device.Ports {
325 if port.Type == voltha.Port_PON_ONU || port.Type == voltha.Port_VENET_ONU {
326 upstreamPorts = append(upstreamPorts, port)
327 } else if port.Type == voltha.Port_ETHERNET_UNI {
328 downstreamPorts = append(downstreamPorts, port)
329 }
330 }
331 //it is possible that the downstream ports are not created, but the flow_decomposition has already
332 //kicked in. In such scenarios, cut short the processing and return.
333 if len(downstreamPorts) == 0 {
334 return fg
335 }
336 // set up the default flows
337 var fa *fu.FlowArgs
338 fa = &fu.FlowArgs{
339 KV: fu.OfpFlowModArgs{"priority": 500},
340 MatchFields: []*ofp.OfpOxmOfbField{
341 fd.InPort(downstreamPorts[0].PortNo),
342 fd.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
343 },
344 Actions: []*ofp.OfpAction{
345 fd.SetField(fd.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | device.Vlan)),
346 },
347 }
348 fg.AddFlow(fd.MkFlowStat(fa))
349
350 fa = &fu.FlowArgs{
351 KV: fu.OfpFlowModArgs{"priority": 500},
352 MatchFields: []*ofp.OfpOxmOfbField{
353 fd.InPort(downstreamPorts[0].PortNo),
354 fd.VlanVid(0),
355 },
356 Actions: []*ofp.OfpAction{
357 fd.PushVlan(0x8100),
358 fd.SetField(fd.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | device.Vlan)),
359 fd.Output(upstreamPorts[0].PortNo),
360 },
361 }
362 fg.AddFlow(fd.MkFlowStat(fa))
363
364 fa = &fu.FlowArgs{
365 KV: fu.OfpFlowModArgs{"priority": 500},
366 MatchFields: []*ofp.OfpOxmOfbField{
367 fd.InPort(upstreamPorts[0].PortNo),
368 fd.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | device.Vlan),
369 },
370 Actions: []*ofp.OfpAction{
371 fd.SetField(fd.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0)),
372 fd.Output(downstreamPorts[0].PortNo),
373 },
374 }
375 fg.AddFlow(fd.MkFlowStat(fa))
376
377 return fg
378}
379
380func (agent *LogicalDeviceAgent) generateDefaultRules() *fu.DeviceRules {
381 rules := fu.NewDeviceRules()
382 var ld *voltha.LogicalDevice
383 var err error
384 if ld, err = agent.getLogicalDevice(); err != nil {
385 log.Warnw("no-logical-device", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
386 return rules
387 }
388
389 deviceNodeIds := agent.deviceGraph.GetDeviceNodeIds()
390 for deviceId, _ := range deviceNodeIds {
391 if deviceId == ld.RootDeviceId {
392 rules.AddFlowsAndGroup(deviceId, agent.rootDeviceDefaultRules())
393 } else {
394 rules.AddFlowsAndGroup(deviceId, agent.leafDeviceDefaultRules(deviceId))
395 }
396 }
397 return rules
398}
399
400func (agent *LogicalDeviceAgent) GetAllDefaultRules() *fu.DeviceRules {
401 // Get latest
402 var lDevice *voltha.LogicalDevice
403 var err error
404 if lDevice, err = agent.getLogicalDevice(); err != nil {
405 return fu.NewDeviceRules()
406 }
407 if agent.DefaultFlowRules == nil { // Nothing setup yet
408 agent.deviceGraph = graph.NewDeviceGraph(agent.deviceMgr.getDevice)
409 agent.deviceGraph.ComputeRoutes(lDevice.Ports)
410 agent.DefaultFlowRules = agent.generateDefaultRules()
411 }
412 return agent.DefaultFlowRules
413}
414
415func (agent *LogicalDeviceAgent) GetWildcardInputPorts(excludePort ...uint32) []uint32 {
416 lPorts := make([]uint32, 0)
417 var exclPort uint32
418 if len(excludePort) == 1 {
419 exclPort = excludePort[0]
420 }
421 if lDevice, _ := agent.getLogicalDevice(); lDevice != nil {
422 for _, port := range lDevice.Ports {
423 if port.OfpPort.PortNo != exclPort {
424 lPorts = append(lPorts, port.OfpPort.PortNo)
425 }
426 }
427 }
428 return lPorts
429}