05a20f961c3e65bff254184d21b0de2a22173078
[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 org.apache.felix.scr.annotations.Activate;
21 import org.apache.felix.scr.annotations.Component;
22 import org.apache.felix.scr.annotations.Deactivate;
23 import org.apache.felix.scr.annotations.Reference;
24 import org.apache.felix.scr.annotations.ReferenceCardinality;
25 import org.onlab.util.Frequency;
26 import org.onosproject.net.AnnotationKeys;
27 import org.onosproject.net.ConnectPoint;
28 import org.onosproject.net.DeviceId;
29 import org.onosproject.net.Link;
30 import org.onosproject.net.OchPort;
31 import org.onosproject.net.OchSignal;
32 import org.onosproject.net.OchSignalType;
33 import org.onosproject.net.OmsPort;
34 import org.onosproject.net.Path;
35 import org.onosproject.net.Port;
36 import org.onosproject.net.device.DeviceService;
37 import org.onosproject.net.intent.Intent;
38 import org.onosproject.net.intent.IntentCompiler;
39 import org.onosproject.net.intent.IntentExtensionService;
40 import org.onosproject.net.intent.OpticalConnectivityIntent;
41 import org.onosproject.net.intent.OpticalPathIntent;
42 import org.onosproject.net.intent.impl.IntentCompilationException;
43 import org.onosproject.net.resource.ResourceAllocation;
44 import org.onosproject.net.resource.ResourceType;
45 import org.onosproject.net.resource.device.DeviceResourceService;
46 import org.onosproject.net.resource.link.DefaultLinkResourceRequest;
47 import org.onosproject.net.resource.link.LambdaResource;
48 import org.onosproject.net.resource.link.LambdaResourceAllocation;
49 import org.onosproject.net.resource.link.LinkResourceAllocations;
50 import org.onosproject.net.resource.link.LinkResourceRequest;
51 import org.onosproject.net.resource.link.LinkResourceService;
52 import org.onosproject.net.topology.LinkWeight;
53 import org.onosproject.net.topology.Topology;
54 import org.onosproject.net.topology.TopologyService;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 import java.util.List;
59 import java.util.Set;
60
61 import static com.google.common.base.Preconditions.checkArgument;
62
63 /**
64  * An intent compiler for {@link org.onosproject.net.intent.OpticalConnectivityIntent}.
65  */
66 @Component(immediate = true)
67 public class OpticalConnectivityIntentCompiler implements IntentCompiler<OpticalConnectivityIntent> {
68
69     protected static final Logger log = LoggerFactory.getLogger(OpticalConnectivityIntentCompiler.class);
70
71     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72     protected IntentExtensionService intentManager;
73
74     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75     protected TopologyService topologyService;
76
77     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78     protected DeviceService deviceService;
79
80     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81     protected LinkResourceService linkResourceService;
82
83     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84     protected DeviceResourceService deviceResourceService;
85
86     @Activate
87     public void activate() {
88         intentManager.registerCompiler(OpticalConnectivityIntent.class, this);
89     }
90
91     @Deactivate
92     public void deactivate() {
93         intentManager.unregisterCompiler(OpticalConnectivityIntent.class);
94     }
95
96     @Override
97     public List<Intent> compile(OpticalConnectivityIntent intent,
98                                 List<Intent> installable,
99                                 Set<LinkResourceAllocations> resources) {
100         // Check if source and destination are optical OCh ports
101         ConnectPoint src = intent.getSrc();
102         ConnectPoint dst = intent.getDst();
103         Port srcPort = deviceService.getPort(src.deviceId(), src.port());
104         Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
105         checkArgument(srcPort instanceof OchPort);
106         checkArgument(dstPort instanceof OchPort);
107
108         log.debug("Compiling optical connectivity intent between {} and {}", src, dst);
109
110         // Reserve OCh ports
111         if (!deviceResourceService.requestPorts(ImmutableSet.of(srcPort, dstPort), intent)) {
112             throw new IntentCompilationException("Unable to reserve ports for intent " + intent);
113         }
114
115         // Calculate available light paths
116         Set<Path> paths = getOpticalPaths(intent);
117
118         // Use first path that can be successfully reserved
119         for (Path path : paths) {
120
121             // Static or dynamic lambda allocation
122             String staticLambda = srcPort.annotations().value(AnnotationKeys.STATIC_LAMBDA);
123             OchPort srcOchPort = (OchPort) srcPort;
124             OchPort dstOchPort = (OchPort) dstPort;
125             OchSignal ochSignal;
126
127             // FIXME: need to actually reserve the lambda for static lambda's
128             if (staticLambda != null) {
129                 ochSignal = new OchSignal(Frequency.ofHz(Long.parseLong(staticLambda)),
130                         srcOchPort.lambda().channelSpacing(),
131                         srcOchPort.lambda().slotGranularity());
132             } else if (!srcOchPort.isTunable() || !dstOchPort.isTunable()) {
133                 // FIXME: also check OCh port
134                 ochSignal = srcOchPort.lambda();
135             } else {
136                 // Request and reserve lambda on path
137                 LinkResourceAllocations linkAllocs = assignWavelength(intent, path);
138                 if (linkAllocs == null) {
139                     continue;
140                 }
141                 LambdaResourceAllocation lambdaAlloc = getWavelength(path, linkAllocs);
142                 OmsPort omsPort = (OmsPort) deviceService.getPort(path.src().deviceId(), path.src().port());
143                 ochSignal = new OchSignal(lambdaAlloc.lambda().toInt(), omsPort.maxFrequency(), omsPort.grid());
144             }
145
146             // Create installable optical path intent
147             // Only support fixed grid for now
148             OchSignalType signalType = OchSignalType.FIXED_GRID;
149
150             Intent newIntent = OpticalPathIntent.builder()
151                     .appId(intent.appId())
152                     .src(intent.getSrc())
153                     .dst(intent.getDst())
154                     .path(path)
155                     .lambda(ochSignal)
156                     .signalType(signalType)
157                     .bidirectional(intent.isBidirectional())
158                     .build();
159
160             return ImmutableList.of(newIntent);
161         }
162
163         // Release port allocations if unsuccessful
164         deviceResourceService.releasePorts(intent.id());
165
166         throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent);
167     }
168
169     /**
170      * Find the lambda allocated to the path.
171      *
172      * @param path the path
173      * @param linkAllocs the link allocations
174      * @return lambda allocated to the given path
175      */
176     private LambdaResourceAllocation getWavelength(Path path, LinkResourceAllocations linkAllocs) {
177         for (Link link : path.links()) {
178             for (ResourceAllocation alloc : linkAllocs.getResourceAllocation(link)) {
179                 if (alloc.type() == ResourceType.LAMBDA) {
180                     return (LambdaResourceAllocation) alloc;
181                 }
182             }
183         }
184
185         return null;
186     }
187
188     /**
189      * Request and reserve first available wavelength across path.
190      *
191      * @param path path in WDM topology
192      * @return first available lambda resource allocation
193      */
194     private LinkResourceAllocations assignWavelength(Intent intent, Path path) {
195         LinkResourceRequest.Builder request =
196                 DefaultLinkResourceRequest.builder(intent.id(), path.links())
197                 .addLambdaRequest();
198
199         LinkResourceAllocations allocations = linkResourceService.requestResources(request.build());
200
201         if (!checkWavelengthContinuity(allocations, path)) {
202             linkResourceService.releaseResources(allocations);
203             return null;
204         }
205
206         return allocations;
207     }
208
209     /**
210      * Checks wavelength continuity constraint across path, i.e., an identical lambda is used on all links.
211      * @return true if wavelength continuity is met, false otherwise
212      */
213     private boolean checkWavelengthContinuity(LinkResourceAllocations allocations, Path path) {
214         if (allocations == null) {
215             return false;
216         }
217
218         LambdaResource lambda = null;
219
220         for (Link link : path.links()) {
221             for (ResourceAllocation alloc : allocations.getResourceAllocation(link)) {
222                 if (alloc.type() == ResourceType.LAMBDA) {
223                     LambdaResource nextLambda = ((LambdaResourceAllocation) alloc).lambda();
224                     if (nextLambda == null) {
225                         return false;
226                     }
227                     if (lambda == null) {
228                         lambda = nextLambda;
229                         continue;
230                     }
231                     if (!lambda.equals(nextLambda)) {
232                         return false;
233                     }
234                 }
235             }
236         }
237
238         return true;
239     }
240
241     private ConnectPoint staticPort(ConnectPoint connectPoint) {
242         Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
243
244         String staticPort = port.annotations().value(AnnotationKeys.STATIC_PORT);
245
246         // FIXME: need a better way to match the port
247         if (staticPort != null) {
248             for (Port p : deviceService.getPorts(connectPoint.deviceId())) {
249                 if (staticPort.equals(p.number().name())) {
250                     return new ConnectPoint(p.element().id(), p.number());
251                 }
252             }
253         }
254
255         return null;
256     }
257
258     /**
259      * Calculates optical paths in WDM topology.
260      *
261      * @param intent optical connectivity intent
262      * @return set of paths in WDM topology
263      */
264     private Set<Path> getOpticalPaths(OpticalConnectivityIntent intent) {
265         // Route in WDM topology
266         Topology topology = topologyService.currentTopology();
267         LinkWeight weight = edge -> {
268             // Disregard inactive or non-optical links
269             if (edge.link().state() == Link.State.INACTIVE) {
270                 return -1;
271             }
272             if (edge.link().type() != Link.Type.OPTICAL) {
273                 return -1;
274             }
275             // Adhere to static port mappings
276             DeviceId srcDeviceId = edge.link().src().deviceId();
277             if (srcDeviceId.equals(intent.getSrc().deviceId())) {
278                 ConnectPoint srcStaticPort = staticPort(intent.getSrc());
279                 if (srcStaticPort != null) {
280                     return srcStaticPort.equals(edge.link().src()) ? 1 : -1;
281                 }
282             }
283             DeviceId dstDeviceId = edge.link().dst().deviceId();
284             if (dstDeviceId.equals(intent.getDst().deviceId())) {
285                 ConnectPoint dstStaticPort = staticPort(intent.getDst());
286                 if (dstStaticPort != null) {
287                     return dstStaticPort.equals(edge.link().dst()) ? 1 : -1;
288                 }
289             }
290
291             return 1;
292         };
293
294         ConnectPoint start = intent.getSrc();
295         ConnectPoint end = intent.getDst();
296         Set<Path> paths = topologyService.getPaths(topology, start.deviceId(),
297                 end.deviceId(), weight);
298
299         return paths;
300     }
301 }