Kent Hagerman | 3136fbd | 2020-05-14 10:30:45 -0400 | [diff] [blame^] | 1 | /* |
| 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 | |
| 17 | package device |
| 18 | |
| 19 | import ( |
| 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 |
| 33 | func (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 | |
| 99 | func (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 |
| 111 | func (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. |
| 117 | func (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 |
| 140 | func (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 |
| 163 | func (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 | } |