blob: 673616073ec63c316b1d2cdfb44ce0f81bac3c52 [file] [log] [blame]
Kent Hagerman3136fbd2020-05-14 10:30:45 -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 */
16
17package device
18
19import (
20 "context"
21 "errors"
22 "fmt"
23
24 "github.com/opencord/voltha-go/rw_core/route"
25 "github.com/opencord/voltha-lib-go/v3/pkg/log"
26 ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
27 "github.com/opencord/voltha-protos/v3/go/voltha"
28 "google.golang.org/grpc/codes"
29 "google.golang.org/grpc/status"
30)
31
32// GetRoute returns route
33func (agent *LogicalAgent) GetRoute(ctx context.Context, ingressPortNo uint32, egressPortNo uint32) ([]route.Hop, error) {
34 logger.Debugw("getting-route", log.Fields{"ingress-port": ingressPortNo, "egress-port": egressPortNo})
35 routes := make([]route.Hop, 0)
36
37 // Note: A port value of 0 is equivalent to a nil port
38
39 // Consider different possibilities
40 if egressPortNo != 0 && ((egressPortNo & 0x7fffffff) == uint32(ofp.OfpPortNo_OFPP_CONTROLLER)) {
41 logger.Debugw("controller-flow", log.Fields{"ingressPortNo": ingressPortNo, "egressPortNo": egressPortNo, "logicalPortsNo": agent.logicalPortsNo})
42 if agent.isNNIPort(ingressPortNo) {
43 //This is a trap on the NNI Port
44 if len(agent.deviceRoutes.Routes) == 0 {
45 // If there are no routes set (usually when the logical device has only NNI port(s), then just return an
46 // route with same IngressHop and EgressHop
47 hop := route.Hop{DeviceID: agent.rootDeviceID, Ingress: ingressPortNo, Egress: ingressPortNo}
48 routes = append(routes, hop)
49 routes = append(routes, hop)
50 return routes, nil
51 }
52 //Return a 'half' route to make the flow decomposer logic happy
53 for routeLink, path := range agent.deviceRoutes.Routes {
54 if agent.isNNIPort(routeLink.Egress) {
55 routes = append(routes, route.Hop{}) // first hop is set to empty
56 routes = append(routes, path[1])
57 return routes, nil
58 }
59 }
60 return nil, fmt.Errorf("no upstream route from:%d to:%d :%w", ingressPortNo, egressPortNo, route.ErrNoRoute)
61 }
62 //treat it as if the output port is the first NNI of the OLT
63 var err error
64 if egressPortNo, err = agent.getFirstNNIPort(); err != nil {
65 logger.Warnw("no-nni-port", log.Fields{"error": err})
66 return nil, err
67 }
68 }
69 //If ingress port is not specified (nil), it may be a wildcarded
70 //route if egress port is OFPP_CONTROLLER or a nni logical port,
71 //in which case we need to create a half-route where only the egress
72 //hop is filled, the first hop is nil
73 if ingressPortNo == 0 && agent.isNNIPort(egressPortNo) {
74 // We can use the 2nd hop of any upstream route, so just find the first upstream:
75 for routeLink, path := range agent.deviceRoutes.Routes {
76 if agent.isNNIPort(routeLink.Egress) {
77 routes = append(routes, route.Hop{}) // first hop is set to empty
78 routes = append(routes, path[1])
79 return routes, nil
80 }
81 }
82 return nil, fmt.Errorf("no upstream route from:%d to:%d :%w", ingressPortNo, egressPortNo, route.ErrNoRoute)
83 }
84 //If egress port is not specified (nil), we can also can return a "half" route
85 if egressPortNo == 0 {
86 for routeLink, path := range agent.deviceRoutes.Routes {
87 if routeLink.Ingress == ingressPortNo {
88 routes = append(routes, path[0])
89 routes = append(routes, route.Hop{})
90 return routes, nil
91 }
92 }
93 return nil, fmt.Errorf("no downstream route from:%d to:%d :%w", ingressPortNo, egressPortNo, route.ErrNoRoute)
94 }
95 // Return the pre-calculated route
96 return agent.getPreCalculatedRoute(ingressPortNo, egressPortNo)
97}
98
99func (agent *LogicalAgent) getPreCalculatedRoute(ingress, egress uint32) ([]route.Hop, error) {
100 logger.Debugw("ROUTE", log.Fields{"len": len(agent.deviceRoutes.Routes)})
101 for routeLink, route := range agent.deviceRoutes.Routes {
102 logger.Debugw("ROUTELINKS", log.Fields{"ingress": ingress, "egress": egress, "routelink": routeLink})
103 if ingress == routeLink.Ingress && egress == routeLink.Egress {
104 return route, nil
105 }
106 }
107 return nil, status.Errorf(codes.FailedPrecondition, "no route from:%d to:%d", ingress, egress)
108}
109
110// GetDeviceRoutes returns device graph
111func (agent *LogicalAgent) GetDeviceRoutes() *route.DeviceRoutes {
112 return agent.deviceRoutes
113}
114
115//generateDeviceRoutesIfNeeded generates the device routes if the logical device has been updated since the last time
116//that device graph was generated.
117func (agent *LogicalAgent) generateDeviceRoutesIfNeeded(ctx context.Context) error {
118 agent.lockDeviceRoutes.Lock()
119 defer agent.lockDeviceRoutes.Unlock()
120
121 ld, err := agent.GetLogicalDevice(ctx)
122 if err != nil {
123 return err
124 }
125
126 if agent.deviceRoutes != nil && agent.deviceRoutes.IsUpToDate(ld) {
127 return nil
128 }
129 logger.Debug("Generation of device route required")
130 if err := agent.buildRoutes(ctx); err != nil {
131 // No Route is not an error
132 if !errors.Is(err, route.ErrNoRoute) {
133 return err
134 }
135 }
136 return nil
137}
138
139//rebuildRoutes rebuilds the device routes
140func (agent *LogicalAgent) buildRoutes(ctx context.Context) error {
141 logger.Debugf("building-routes", log.Fields{"logical-device-id": agent.logicalDeviceID})
142 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
143 return err
144 }
145 defer agent.requestQueue.RequestComplete()
146
147 if agent.deviceRoutes == nil {
148 agent.deviceRoutes = route.NewDeviceRoutes(agent.logicalDeviceID, agent.deviceMgr.getDevice)
149 }
150 // Get all the logical ports on that logical device
151 lDevice := agent.getLogicalDeviceWithoutLock()
152
153 if err := agent.deviceRoutes.ComputeRoutes(ctx, lDevice.Ports); err != nil {
154 return err
155 }
156 if err := agent.deviceRoutes.Print(); err != nil {
157 return err
158 }
159 return nil
160}
161
162//updateRoutes updates the device routes
163func (agent *LogicalAgent) updateRoutes(ctx context.Context, lp *voltha.LogicalPort) error {
164 logger.Debugw("updateRoutes", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
165 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
166 return err
167 }
168 defer agent.requestQueue.RequestComplete()
169
170 if agent.deviceRoutes == nil {
171 agent.deviceRoutes = route.NewDeviceRoutes(agent.logicalDeviceID, agent.deviceMgr.getDevice)
172 }
173 if err := agent.deviceRoutes.AddPort(ctx, lp, agent.logicalDevice.Ports); err != nil {
174 return err
175 }
176 if err := agent.deviceRoutes.Print(); err != nil {
177 return err
178 }
179 return nil
180}