99f58df7db02feff4ad8dce947391bf4b02ae223
[onosfw.git] /
1 /*
2  * Copyright 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.Sets;
19 import org.apache.commons.lang3.tuple.Pair;
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.Modified;
24 import org.apache.felix.scr.annotations.Property;
25 import org.apache.felix.scr.annotations.Reference;
26 import org.apache.felix.scr.annotations.ReferenceCardinality;
27 import org.onlab.util.Tools;
28 import org.onosproject.cfg.ComponentConfigService;
29 import org.onosproject.core.ApplicationId;
30 import org.onosproject.core.CoreService;
31 import org.onosproject.net.AnnotationKeys;
32 import org.onosproject.net.ConnectPoint;
33 import org.onosproject.net.OchPort;
34 import org.onosproject.net.OduCltPort;
35 import org.onosproject.net.OduSignalType;
36 import org.onosproject.net.Port;
37 import org.onosproject.net.device.DeviceService;
38 import org.onosproject.net.flow.DefaultFlowRule;
39 import org.onosproject.net.flow.DefaultTrafficSelector;
40 import org.onosproject.net.flow.DefaultTrafficTreatment;
41 import org.onosproject.net.flow.FlowRule;
42 import org.onosproject.net.flow.TrafficSelector;
43 import org.onosproject.net.flow.TrafficTreatment;
44 import org.onosproject.net.intent.FlowRuleIntent;
45 import org.onosproject.net.intent.Intent;
46 import org.onosproject.net.intent.IntentCompiler;
47 import org.onosproject.net.intent.IntentExtensionService;
48 import org.onosproject.net.intent.IntentId;
49 import org.onosproject.net.intent.IntentService;
50 import org.onosproject.net.intent.OpticalCircuitIntent;
51 import org.onosproject.net.intent.OpticalConnectivityIntent;
52 import org.onosproject.net.intent.impl.IntentCompilationException;
53 import org.onosproject.net.resource.device.DeviceResourceService;
54 import org.onosproject.net.resource.link.LinkResourceAllocations;
55 import org.osgi.service.component.ComponentContext;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 import java.util.Collections;
60 import java.util.Dictionary;
61 import java.util.LinkedList;
62 import java.util.List;
63 import java.util.Set;
64
65 import static com.google.common.base.Preconditions.checkArgument;
66
67 /**
68  * An intent compiler for {@link org.onosproject.net.intent.OpticalCircuitIntent}.
69  */
70 @Component(immediate = true)
71 public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircuitIntent> {
72
73     private static final Logger log = LoggerFactory.getLogger(OpticalCircuitIntentCompiler.class);
74
75     private static final int DEFAULT_MAX_CAPACITY = 10;
76
77     @Property(name = "maxCapacity", intValue = DEFAULT_MAX_CAPACITY,
78             label = "Maximum utilization of an optical connection.")
79
80     private int maxCapacity = DEFAULT_MAX_CAPACITY;
81
82     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83     protected ComponentConfigService cfgService;
84
85     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86     protected IntentExtensionService intentManager;
87
88     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89     protected CoreService coreService;
90
91     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92     protected DeviceService deviceService;
93
94     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95     protected DeviceResourceService deviceResourceService;
96
97     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98     protected IntentService intentService;
99
100     private ApplicationId appId;
101
102     @Modified
103     public void modified(ComponentContext context) {
104         Dictionary properties = context.getProperties();
105
106         //TODO for reduction check if the new capacity is smaller than the size of the current mapping
107         String propertyString = Tools.get(properties, "maxCapacity");
108
109         //Ignore if propertyString is empty
110         if (!propertyString.isEmpty()) {
111             try {
112                 int temp = Integer.parseInt(propertyString);
113                 //Ensure value is non-negative but allow zero as a way to shutdown the link
114                 if (temp >= 0) {
115                     maxCapacity = temp;
116                 }
117             } catch (NumberFormatException e) {
118                 //Malformed arguments lead to no change of value (user should be notified of error)
119               log.error("The value '{}' for maxCapacity was not parsable as an integer.", propertyString, e);
120             }
121         } else {
122             //Notify of empty value but do not return (other properties will also go in this function)
123             log.error("The value for maxCapacity was set to an empty value.");
124         }
125
126     }
127
128     @Activate
129     public void activate(ComponentContext context) {
130         appId = coreService.registerApplication("org.onosproject.net.intent");
131         intentManager.registerCompiler(OpticalCircuitIntent.class, this);
132         cfgService.registerProperties(getClass());
133         modified(context);
134     }
135
136     @Deactivate
137     public void deactivate() {
138         intentManager.unregisterCompiler(OpticalCircuitIntent.class);
139         cfgService.unregisterProperties(getClass(), false);
140     }
141
142     @Override
143     public List<Intent> compile(OpticalCircuitIntent intent, List<Intent> installable,
144                                 Set<LinkResourceAllocations> resources) {
145         // Check if ports are OduClt ports
146         ConnectPoint src = intent.getSrc();
147         ConnectPoint dst = intent.getDst();
148         Port srcPort = deviceService.getPort(src.deviceId(), src.port());
149         Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
150         checkArgument(srcPort instanceof OduCltPort);
151         checkArgument(dstPort instanceof OduCltPort);
152
153         log.debug("Compiling optical circuit intent between {} and {}", src, dst);
154
155         // Reserve OduClt ports
156         if (!deviceResourceService.requestPorts(Sets.newHashSet(srcPort, dstPort), intent)) {
157             throw new IntentCompilationException("Unable to reserve ports for intent " + intent);
158         }
159
160         LinkedList<Intent> intents = new LinkedList<>();
161
162         FlowRuleIntent circuitIntent;
163         OpticalConnectivityIntent connIntent = findOpticalConnectivityIntent(intent);
164
165         // Create optical connectivity intent if needed
166         if (connIntent == null) {
167             // Find OCh ports with available resources
168             Pair<OchPort, OchPort> ochPorts = findPorts(intent);
169
170             if (ochPorts == null) {
171                 return Collections.emptyList();
172             }
173
174             // Create optical connectivity intent
175             ConnectPoint srcCP = new ConnectPoint(src.elementId(), ochPorts.getLeft().number());
176             ConnectPoint dstCP = new ConnectPoint(dst.elementId(), ochPorts.getRight().number());
177             // FIXME: hardcoded ODU signal type
178             connIntent = OpticalConnectivityIntent.builder()
179                     .appId(appId)
180                     .src(srcCP)
181                     .dst(dstCP)
182                     .signalType(OduSignalType.ODU4)
183                     .bidirectional(intent.isBidirectional())
184                     .build();
185             intentService.submit(connIntent);
186         }
187
188         // Create optical circuit intent
189         List<FlowRule> rules = new LinkedList<>();
190         rules.add(connectPorts(src, connIntent.getSrc(), intent.priority()));
191         rules.add(connectPorts(connIntent.getDst(), dst, intent.priority()));
192
193         // Create flow rules for reverse path
194         if (intent.isBidirectional()) {
195             rules.add(connectPorts(connIntent.getSrc(), src, intent.priority()));
196             rules.add(connectPorts(dst, connIntent.getDst(), intent.priority()));
197         }
198
199         circuitIntent = new FlowRuleIntent(appId, rules, intent.resources());
200
201         // Save circuit to connectivity intent mapping
202         deviceResourceService.requestMapping(connIntent.id(), intent.id());
203         intents.add(circuitIntent);
204
205         return intents;
206     }
207
208     /**
209      * Checks if current allocations on given resource can satisfy request.
210      * If the resource is null, return true.
211      *
212      * @param request the intent making the request
213      * @param resource the resource on which to map the intent
214      * @return true if the resource can accept the request, false otherwise
215      */
216     private boolean isAvailable(Intent request, IntentId resource) {
217         if (resource == null) {
218             return true;
219         }
220
221         Set<IntentId> mapping = deviceResourceService.getMapping(resource);
222
223         if (mapping == null) {
224             return true;
225         }
226
227         return mapping.size() < maxCapacity;
228     }
229
230     private boolean isAllowed(OpticalCircuitIntent circuitIntent, OpticalConnectivityIntent connIntent) {
231         ConnectPoint srcStaticPort = staticPort(circuitIntent.getSrc());
232         if (srcStaticPort != null) {
233             if (!srcStaticPort.equals(connIntent.getSrc())) {
234                 return false;
235             }
236         }
237
238         ConnectPoint dstStaticPort = staticPort(circuitIntent.getDst());
239         if (dstStaticPort != null) {
240             if (!dstStaticPort.equals(connIntent.getDst())) {
241                 return false;
242             }
243         }
244
245         return true;
246     }
247
248     /**
249      * Returns existing and available optical connectivity intent that matches the given circuit intent.
250      *
251      * @param circuitIntent optical circuit intent
252      * @return existing optical connectivity intent, null otherwise.
253      */
254     private OpticalConnectivityIntent findOpticalConnectivityIntent(OpticalCircuitIntent circuitIntent) {
255         for (Intent intent : intentService.getIntents()) {
256             if (!(intent instanceof OpticalConnectivityIntent)) {
257                 continue;
258             }
259
260             OpticalConnectivityIntent connIntent = (OpticalConnectivityIntent) intent;
261
262             ConnectPoint src = circuitIntent.getSrc();
263             ConnectPoint dst = circuitIntent.getDst();
264             // Ignore if the intents don't have identical src and dst devices
265             if (!src.deviceId().equals(connIntent.getSrc().deviceId()) &&
266                     !dst.deviceId().equals(connIntent.getDst().deviceId())) {
267                 continue;
268             }
269
270             if (!isAllowed(circuitIntent, connIntent)) {
271                 continue;
272             }
273
274             if (isAvailable(circuitIntent, connIntent.id())) {
275                 return connIntent;
276             }
277         }
278
279         return null;
280     }
281
282     private ConnectPoint staticPort(ConnectPoint connectPoint) {
283         Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
284
285         String staticPort = port.annotations().value(AnnotationKeys.STATIC_PORT);
286
287         // FIXME: need a better way to match the port
288         if (staticPort != null) {
289             for (Port p : deviceService.getPorts(connectPoint.deviceId())) {
290                 if (staticPort.equals(p.number().name())) {
291                     return new ConnectPoint(p.element().id(), p.number());
292                 }
293             }
294         }
295
296         return null;
297     }
298
299     private OchPort findAvailableOchPort(ConnectPoint oduPort, OpticalCircuitIntent circuitIntent) {
300         // First see if the port mappings are constrained
301         ConnectPoint ochCP = staticPort(oduPort);
302
303         if (ochCP != null) {
304             OchPort ochPort = (OchPort) deviceService.getPort(ochCP.deviceId(), ochCP.port());
305             IntentId intentId = deviceResourceService.getAllocations(ochPort);
306             if (isAvailable(circuitIntent, intentId)) {
307                 return ochPort;
308             }
309         }
310
311         // No port constraints, so find any port that works
312         List<Port> ports = deviceService.getPorts(oduPort.deviceId());
313
314         for (Port port : ports) {
315             if (!(port instanceof OchPort)) {
316                 continue;
317             }
318
319             IntentId intentId = deviceResourceService.getAllocations(port);
320             if (isAvailable(circuitIntent, intentId)) {
321                 return (OchPort) port;
322             }
323         }
324
325         return null;
326     }
327
328     private Pair<OchPort, OchPort> findPorts(OpticalCircuitIntent intent) {
329
330         OchPort srcPort = findAvailableOchPort(intent.getSrc(), intent);
331         if (srcPort == null) {
332             return null;
333         }
334
335         OchPort dstPort = findAvailableOchPort(intent.getDst(), intent);
336         if (dstPort == null) {
337             return null;
338         }
339
340         return Pair.of(srcPort, dstPort);
341     }
342
343     /**
344      * Builds flow rule for mapping between two ports.
345      *
346      * @param src source port
347      * @param dst destination port
348      * @return flow rules
349      */
350     private FlowRule connectPorts(ConnectPoint src, ConnectPoint dst, int priority) {
351         checkArgument(src.deviceId().equals(dst.deviceId()));
352
353         TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
354         TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
355
356         selectorBuilder.matchInPort(src.port());
357         //selectorBuilder.add(Criteria.matchCltSignalType)
358         treatmentBuilder.setOutput(dst.port());
359         //treatmentBuilder.add(Instructions.modL1OduSignalType)
360
361         FlowRule flowRule = DefaultFlowRule.builder()
362                 .forDevice(src.deviceId())
363                 .withSelector(selectorBuilder.build())
364                 .withTreatment(treatmentBuilder.build())
365                 .withPriority(priority)
366                 .fromApp(appId)
367                 .makePermanent()
368                 .build();
369
370         return flowRule;
371     }
372 }