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