eb5b4af83ad372e0ea5878517333a7ded469108a
[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 org.apache.felix.scr.annotations.Activate;
20 import org.apache.felix.scr.annotations.Deactivate;
21 import org.apache.felix.scr.annotations.Reference;
22 import org.apache.felix.scr.annotations.ReferenceCardinality;
23 import org.onlab.util.Frequency;
24 import org.onosproject.net.AnnotationKeys;
25 import org.onosproject.net.ConnectPoint;
26 import org.onosproject.net.DeviceId;
27 import org.onosproject.net.Link;
28 import org.onosproject.net.OchPort;
29 import org.onosproject.net.OchSignal;
30 import org.onosproject.net.OchSignalType;
31 import org.onosproject.net.OmsPort;
32 import org.onosproject.net.Path;
33 import org.onosproject.net.Port;
34 import org.onosproject.net.device.DeviceService;
35 import org.onosproject.net.intent.Intent;
36 import org.onosproject.net.intent.IntentCompiler;
37 import org.onosproject.net.intent.IntentExtensionService;
38 import org.onosproject.net.intent.OpticalConnectivityIntent;
39 import org.onosproject.net.intent.OpticalPathIntent;
40 import org.onosproject.net.intent.impl.IntentCompilationException;
41 import org.onosproject.net.newresource.ResourcePath;
42 import org.onosproject.net.newresource.ResourceService;
43 import org.onosproject.net.resource.ResourceType;
44 import org.onosproject.net.resource.link.DefaultLinkResourceRequest;
45 import org.onosproject.net.resource.link.LambdaResource;
46 import org.onosproject.net.resource.link.LambdaResourceAllocation;
47 import org.onosproject.net.resource.link.LinkResourceAllocations;
48 import org.onosproject.net.resource.link.LinkResourceRequest;
49 import org.onosproject.net.resource.link.LinkResourceService;
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.List;
57 import java.util.Set;
58 import java.util.stream.Collectors;
59
60 import static com.google.common.base.Preconditions.checkArgument;
61
62 /**
63  * An intent compiler for {@link org.onosproject.net.intent.OpticalConnectivityIntent}.
64  */
65 // For now, remove component designation until dependency on the new resource manager is available.
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 ResourceService resourceService;
82
83     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84     protected LinkResourceService linkResourceService;
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         ResourcePath srcPortPath = new ResourcePath(src.deviceId(), src.port());
112         ResourcePath dstPortPath = new ResourcePath(dst.deviceId(), dst.port());
113         List<org.onosproject.net.newresource.ResourceAllocation> allocation =
114                 resourceService.allocate(intent.id(), srcPortPath, dstPortPath);
115         if (allocation.isEmpty()) {
116             throw new IntentCompilationException("Unable to reserve ports for intent " + intent);
117         }
118
119         // Calculate available light paths
120         Set<Path> paths = getOpticalPaths(intent);
121
122         // Use first path that can be successfully reserved
123         for (Path path : paths) {
124
125             // Static or dynamic lambda allocation
126             String staticLambda = srcPort.annotations().value(AnnotationKeys.STATIC_LAMBDA);
127             OchPort srcOchPort = (OchPort) srcPort;
128             OchPort dstOchPort = (OchPort) dstPort;
129             OchSignal ochSignal;
130
131             // FIXME: need to actually reserve the lambda for static lambda's
132             if (staticLambda != null) {
133                 ochSignal = new OchSignal(Frequency.ofHz(Long.parseLong(staticLambda)),
134                         srcOchPort.lambda().channelSpacing(),
135                         srcOchPort.lambda().slotGranularity());
136             } else if (!srcOchPort.isTunable() || !dstOchPort.isTunable()) {
137                 // FIXME: also check OCh port
138                 ochSignal = srcOchPort.lambda();
139             } else {
140                 // Request and reserve lambda on path
141                 LinkResourceAllocations linkAllocs = assignWavelength(intent, path);
142                 if (linkAllocs == null) {
143                     continue;
144                 }
145                 LambdaResourceAllocation lambdaAlloc = getWavelength(path, linkAllocs);
146                 OmsPort omsPort = (OmsPort) deviceService.getPort(path.src().deviceId(), path.src().port());
147                 ochSignal = new OchSignal(lambdaAlloc.lambda().toInt(), omsPort.maxFrequency(), omsPort.grid());
148             }
149
150             // Create installable optical path intent
151             // Only support fixed grid for now
152             OchSignalType signalType = OchSignalType.FIXED_GRID;
153
154             Intent newIntent = OpticalPathIntent.builder()
155                     .appId(intent.appId())
156                     .src(intent.getSrc())
157                     .dst(intent.getDst())
158                     .path(path)
159                     .lambda(ochSignal)
160                     .signalType(signalType)
161                     .bidirectional(intent.isBidirectional())
162                     .build();
163
164             return ImmutableList.of(newIntent);
165         }
166
167         // Release port allocations if unsuccessful
168         resourceService.release(intent.id());
169
170         throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent);
171     }
172
173     /**
174      * Find the lambda allocated to the path.
175      *
176      * @param path the path
177      * @param linkAllocs the link allocations
178      * @return lambda allocated to the given path
179      */
180     private LambdaResourceAllocation getWavelength(Path path, LinkResourceAllocations linkAllocs) {
181         return path.links().stream()
182                 .flatMap(x -> linkAllocs.getResourceAllocation(x).stream())
183                 .filter(x -> x.type() == ResourceType.LAMBDA)
184                 .findFirst()
185                 .map(x -> (LambdaResourceAllocation) x)
186                 .orElse(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         List<LambdaResource> lambdas = path.links().stream()
220                 .flatMap(x -> allocations.getResourceAllocation(x).stream())
221                 .filter(x -> x.type() == ResourceType.LAMBDA)
222                 .map(x -> ((LambdaResourceAllocation) x).lambda())
223                 .collect(Collectors.toList());
224
225         LambdaResource lambda = null;
226         for (LambdaResource nextLambda: lambdas) {
227             if (nextLambda == null) {
228                 return false;
229             }
230             if (lambda == null) {
231                 lambda = nextLambda;
232                 continue;
233             }
234             if (!lambda.equals(nextLambda)) {
235                 return false;
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 = edge -> {
269             // Disregard inactive or non-optical links
270             if (edge.link().state() == Link.State.INACTIVE) {
271                 return -1;
272             }
273             if (edge.link().type() != Link.Type.OPTICAL) {
274                 return -1;
275             }
276             // Adhere to static port mappings
277             DeviceId srcDeviceId = edge.link().src().deviceId();
278             if (srcDeviceId.equals(intent.getSrc().deviceId())) {
279                 ConnectPoint srcStaticPort = staticPort(intent.getSrc());
280                 if (srcStaticPort != null) {
281                     return srcStaticPort.equals(edge.link().src()) ? 1 : -1;
282                 }
283             }
284             DeviceId dstDeviceId = edge.link().dst().deviceId();
285             if (dstDeviceId.equals(intent.getDst().deviceId())) {
286                 ConnectPoint dstStaticPort = staticPort(intent.getDst());
287                 if (dstStaticPort != null) {
288                     return dstStaticPort.equals(edge.link().dst()) ? 1 : -1;
289                 }
290             }
291
292             return 1;
293         };
294
295         ConnectPoint start = intent.getSrc();
296         ConnectPoint end = intent.getDst();
297         Set<Path> paths = topologyService.getPaths(topology, start.deviceId(),
298                 end.deviceId(), weight);
299
300         return paths;
301     }
302 }