2941ddba9d235796aa3465fceebf1731ab7153e9
[onosfw.git] /
1 /*
2  * Copyright 2014-2015 Open Networking Laboratory
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 package org.onosproject.net.intent.impl.compiler;
17
18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.ImmutableSet;
20 import com.google.common.collect.Iterables;
21 import com.google.common.collect.Sets;
22 import org.apache.felix.scr.annotations.Activate;
23 import org.apache.felix.scr.annotations.Component;
24 import org.apache.felix.scr.annotations.Deactivate;
25 import org.apache.felix.scr.annotations.Reference;
26 import org.apache.felix.scr.annotations.ReferenceCardinality;
27 import org.onlab.util.Frequency;
28 import org.onosproject.net.AnnotationKeys;
29 import org.onosproject.net.ConnectPoint;
30 import org.onosproject.net.DeviceId;
31 import org.onosproject.net.IndexedLambda;
32 import org.onosproject.net.Link;
33 import org.onosproject.net.OchPort;
34 import org.onosproject.net.OchSignal;
35 import org.onosproject.net.OchSignalType;
36 import org.onosproject.net.OmsPort;
37 import org.onosproject.net.Path;
38 import org.onosproject.net.Port;
39 import org.onosproject.net.device.DeviceService;
40 import org.onosproject.net.intent.Intent;
41 import org.onosproject.net.intent.IntentCompiler;
42 import org.onosproject.net.intent.IntentExtensionService;
43 import org.onosproject.net.intent.OpticalConnectivityIntent;
44 import org.onosproject.net.intent.OpticalPathIntent;
45 import org.onosproject.net.intent.impl.IntentCompilationException;
46 import org.onosproject.net.newresource.ResourceAllocation;
47 import org.onosproject.net.newresource.ResourcePath;
48 import org.onosproject.net.newresource.ResourceService;
49 import org.onosproject.net.resource.link.LinkResourceAllocations;
50 import org.onosproject.net.topology.LinkWeight;
51 import org.onosproject.net.topology.Topology;
52 import org.onosproject.net.topology.TopologyService;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 import java.util.Collections;
57 import java.util.List;
58 import java.util.Set;
59 import java.util.stream.Collectors;
60
61 import static com.google.common.base.Preconditions.checkArgument;
62 import static org.onosproject.net.LinkKey.linkKey;
63
64 /**
65  * An intent compiler for {@link org.onosproject.net.intent.OpticalConnectivityIntent}.
66  */
67 // For now, remove component designation until dependency on the new resource manager is available.
68 @Component(immediate = true)
69 public class OpticalConnectivityIntentCompiler implements IntentCompiler<OpticalConnectivityIntent> {
70
71     protected static final Logger log = LoggerFactory.getLogger(OpticalConnectivityIntentCompiler.class);
72
73     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74     protected IntentExtensionService intentManager;
75
76     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77     protected TopologyService topologyService;
78
79     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80     protected DeviceService deviceService;
81
82     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83     protected ResourceService resourceService;
84
85     @Activate
86     public void activate() {
87         intentManager.registerCompiler(OpticalConnectivityIntent.class, this);
88     }
89
90     @Deactivate
91     public void deactivate() {
92         intentManager.unregisterCompiler(OpticalConnectivityIntent.class);
93     }
94
95     @Override
96     public List<Intent> compile(OpticalConnectivityIntent intent,
97                                 List<Intent> installable,
98                                 Set<LinkResourceAllocations> resources) {
99         // Check if source and destination are optical OCh ports
100         ConnectPoint src = intent.getSrc();
101         ConnectPoint dst = intent.getDst();
102         Port srcPort = deviceService.getPort(src.deviceId(), src.port());
103         Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
104         checkArgument(srcPort instanceof OchPort);
105         checkArgument(dstPort instanceof OchPort);
106
107         log.debug("Compiling optical connectivity intent between {} and {}", src, dst);
108
109         // Reserve OCh ports
110         ResourcePath srcPortPath = ResourcePath.discrete(src.deviceId(), src.port());
111         ResourcePath dstPortPath = ResourcePath.discrete(dst.deviceId(), dst.port());
112         List<org.onosproject.net.newresource.ResourceAllocation> allocation =
113                 resourceService.allocate(intent.id(), srcPortPath, dstPortPath);
114         if (allocation.isEmpty()) {
115             throw new IntentCompilationException("Unable to reserve ports for intent " + intent);
116         }
117
118         // Calculate available light paths
119         Set<Path> paths = getOpticalPaths(intent);
120
121         // Use first path that can be successfully reserved
122         for (Path path : paths) {
123
124             // Static or dynamic lambda allocation
125             String staticLambda = srcPort.annotations().value(AnnotationKeys.STATIC_LAMBDA);
126             OchPort srcOchPort = (OchPort) srcPort;
127             OchPort dstOchPort = (OchPort) dstPort;
128             OchSignal ochSignal;
129
130             // FIXME: need to actually reserve the lambda for static lambda's
131             if (staticLambda != null) {
132                 ochSignal = new OchSignal(Frequency.ofHz(Long.parseLong(staticLambda)),
133                         srcOchPort.lambda().channelSpacing(),
134                         srcOchPort.lambda().slotGranularity());
135             } else if (!srcOchPort.isTunable() || !dstOchPort.isTunable()) {
136                 // FIXME: also check OCh port
137                 ochSignal = srcOchPort.lambda();
138             } else {
139                 // Request and reserve lambda on path
140                 IndexedLambda lambda = assignWavelength(intent, path);
141                 if (lambda == null) {
142                     continue;
143                 }
144                 OmsPort omsPort = (OmsPort) deviceService.getPort(path.src().deviceId(), path.src().port());
145                 ochSignal = new OchSignal((int) lambda.index(), omsPort.maxFrequency(), omsPort.grid());
146             }
147
148             // Create installable optical path intent
149             // Only support fixed grid for now
150             OchSignalType signalType = OchSignalType.FIXED_GRID;
151
152             Intent newIntent = OpticalPathIntent.builder()
153                     .appId(intent.appId())
154                     .src(intent.getSrc())
155                     .dst(intent.getDst())
156                     .path(path)
157                     .lambda(ochSignal)
158                     .signalType(signalType)
159                     .bidirectional(intent.isBidirectional())
160                     .build();
161
162             return ImmutableList.of(newIntent);
163         }
164
165         // Release port allocations if unsuccessful
166         resourceService.release(intent.id());
167
168         throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent);
169     }
170
171     /**
172      * Request and reserve first available wavelength across path.
173      *
174      * @param path path in WDM topology
175      * @return first available lambda allocated
176      */
177     private IndexedLambda assignWavelength(Intent intent, Path path) {
178         Set<IndexedLambda> lambdas = findCommonLambdasOverLinks(path.links());
179         if (lambdas.isEmpty()) {
180             return null;
181         }
182
183         IndexedLambda minLambda = findFirstLambda(lambdas);
184         List<ResourcePath> lambdaResources = path.links().stream()
185                 .map(x -> ResourcePath.discrete(linkKey(x.src(), x.dst())))
186                 .map(x -> x.child(minLambda))
187                 .collect(Collectors.toList());
188
189         List<ResourceAllocation> allocations = resourceService.allocate(intent.id(), lambdaResources);
190         if (allocations.isEmpty()) {
191             log.info("Resource allocation for {} failed (resource request: {})", intent, lambdaResources);
192             return null;
193         }
194
195         return minLambda;
196     }
197
198     private Set<IndexedLambda> findCommonLambdasOverLinks(List<Link> links) {
199         return links.stream()
200                 .map(x -> ResourcePath.discrete(linkKey(x.src(), x.dst())))
201                 .map(resourceService::getAvailableResources)
202                 .map(x -> Iterables.filter(x, r -> r.last() instanceof IndexedLambda))
203                 .map(x -> Iterables.transform(x, r -> (IndexedLambda) r.last()))
204                 .map(x -> (Set<IndexedLambda>) ImmutableSet.copyOf(x))
205                 .reduce(Sets::intersection)
206                 .orElse(Collections.emptySet());
207     }
208
209     private IndexedLambda findFirstLambda(Set<IndexedLambda> lambdas) {
210         return lambdas.stream()
211                 .findFirst()
212                 .get();
213     }
214
215     private ConnectPoint staticPort(ConnectPoint connectPoint) {
216         Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
217
218         String staticPort = port.annotations().value(AnnotationKeys.STATIC_PORT);
219
220         // FIXME: need a better way to match the port
221         if (staticPort != null) {
222             for (Port p : deviceService.getPorts(connectPoint.deviceId())) {
223                 if (staticPort.equals(p.number().name())) {
224                     return new ConnectPoint(p.element().id(), p.number());
225                 }
226             }
227         }
228
229         return null;
230     }
231
232     /**
233      * Calculates optical paths in WDM topology.
234      *
235      * @param intent optical connectivity intent
236      * @return set of paths in WDM topology
237      */
238     private Set<Path> getOpticalPaths(OpticalConnectivityIntent intent) {
239         // Route in WDM topology
240         Topology topology = topologyService.currentTopology();
241         LinkWeight weight = edge -> {
242             // Disregard inactive or non-optical links
243             if (edge.link().state() == Link.State.INACTIVE) {
244                 return -1;
245             }
246             if (edge.link().type() != Link.Type.OPTICAL) {
247                 return -1;
248             }
249             // Adhere to static port mappings
250             DeviceId srcDeviceId = edge.link().src().deviceId();
251             if (srcDeviceId.equals(intent.getSrc().deviceId())) {
252                 ConnectPoint srcStaticPort = staticPort(intent.getSrc());
253                 if (srcStaticPort != null) {
254                     return srcStaticPort.equals(edge.link().src()) ? 1 : -1;
255                 }
256             }
257             DeviceId dstDeviceId = edge.link().dst().deviceId();
258             if (dstDeviceId.equals(intent.getDst().deviceId())) {
259                 ConnectPoint dstStaticPort = staticPort(intent.getDst());
260                 if (dstStaticPort != null) {
261                     return dstStaticPort.equals(edge.link().dst()) ? 1 : -1;
262                 }
263             }
264
265             return 1;
266         };
267
268         ConnectPoint start = intent.getSrc();
269         ConnectPoint end = intent.getDst();
270         Set<Path> paths = topologyService.getPaths(topology, start.deviceId(),
271                 end.deviceId(), weight);
272
273         return paths;
274     }
275 }