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