blob: 926b8e17c6db3812e8fe4870651addc30003c131 [file] [log] [blame]
Marc De Leenheer8c2caac2015-05-28 16:37:33 -07001/*
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 */
16package org.onosproject.net.intent.impl.compiler;
17
18import org.apache.commons.lang3.tuple.Pair;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -070022import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070024import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -070026import org.onlab.util.Tools;
27import org.onosproject.cfg.ComponentConfigService;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070028import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
Marc De Leenheer723f5532015-06-03 20:16:17 -070030import org.onosproject.net.AnnotationKeys;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070031import org.onosproject.net.ConnectPoint;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070032import org.onosproject.net.OchPort;
33import org.onosproject.net.OduCltPort;
34import org.onosproject.net.OduSignalType;
35import org.onosproject.net.Port;
36import org.onosproject.net.device.DeviceService;
37import org.onosproject.net.flow.DefaultFlowRule;
38import org.onosproject.net.flow.DefaultTrafficSelector;
39import org.onosproject.net.flow.DefaultTrafficTreatment;
40import org.onosproject.net.flow.FlowRule;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
43import org.onosproject.net.intent.FlowRuleIntent;
44import org.onosproject.net.intent.Intent;
45import org.onosproject.net.intent.IntentCompiler;
46import org.onosproject.net.intent.IntentExtensionService;
47import org.onosproject.net.intent.IntentId;
48import org.onosproject.net.intent.IntentService;
49import org.onosproject.net.intent.OpticalCircuitIntent;
50import org.onosproject.net.intent.OpticalConnectivityIntent;
51import org.onosproject.net.intent.impl.IntentCompilationException;
52import org.onosproject.net.resource.device.DeviceResourceService;
53import org.onosproject.net.resource.link.LinkResourceAllocations;
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -070054import org.osgi.service.component.ComponentContext;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070055import org.slf4j.Logger;
56import org.slf4j.LoggerFactory;
57
58import java.util.Arrays;
59import java.util.Collections;
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -070060import java.util.Dictionary;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070061import java.util.HashSet;
62import java.util.LinkedList;
63import java.util.List;
64import java.util.Set;
65
66import static com.google.common.base.Preconditions.checkArgument;
67
68/**
69 * An intent compiler for {@link org.onosproject.net.intent.OpticalCircuitIntent}.
70 */
71@Component(immediate = true)
72public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircuitIntent> {
73
74 private static final Logger log = LoggerFactory.getLogger(OpticalCircuitIntentCompiler.class);
75
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -070076 private static final int DEFAULT_MAX_CAPACITY = 10;
77
78 @Property(name = "maxCapacity", intValue = DEFAULT_MAX_CAPACITY,
79 label = "Maximum utilization of an optical connection.")
80
81 private int maxCapacity = DEFAULT_MAX_CAPACITY;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected ComponentConfigService cfgService;
85
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected IntentExtensionService intentManager;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected CoreService coreService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected DeviceService deviceService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected DeviceResourceService deviceResourceService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected IntentService intentService;
100
101 private ApplicationId appId;
102
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700103 @Modified
104 public void modified(ComponentContext context) {
105 Dictionary properties = context.getProperties();
106
107 //TODO for reduction check if the new capacity is smaller than the size of the current mapping
108 String propertyString = Tools.get(properties, "maxCapacity");
109
110 //Ignore if propertyString is empty
111 if (!propertyString.isEmpty()) {
112 try {
113 int temp = Integer.parseInt(propertyString);
114 //Ensure value is non-negative but allow zero as a way to shutdown the link
115 if (temp >= 0) {
116 maxCapacity = temp;
117 }
118 } catch (NumberFormatException e) {
119 //Malformed arguments lead to no change of value (user should be notified of error)
120 log.error("The value '{}' for maxCapacity was not parsable as an integer.", propertyString, e);
121 }
122 } else {
123 //Notify of empty value but do not return (other properties will also go in this function)
124 log.error("The value for maxCapacity was set to an empty value.");
125 }
126
127 }
128
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700129 @Activate
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700130 public void activate(ComponentContext context) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700131 appId = coreService.registerApplication("org.onosproject.net.intent");
132 intentManager.registerCompiler(OpticalCircuitIntent.class, this);
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700133 cfgService.registerProperties(getClass());
134 modified(context);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700135 }
136
137 @Deactivate
138 public void deactivate() {
139 intentManager.unregisterCompiler(OpticalCircuitIntent.class);
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700140 cfgService.unregisterProperties(getClass(), false);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700141 }
142
143 @Override
144 public List<Intent> compile(OpticalCircuitIntent intent, List<Intent> installable,
145 Set<LinkResourceAllocations> resources) {
146 // Check if ports are OduClt ports
147 ConnectPoint src = intent.getSrc();
148 ConnectPoint dst = intent.getDst();
149 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
150 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
151 checkArgument(srcPort instanceof OduCltPort);
152 checkArgument(dstPort instanceof OduCltPort);
153
154 log.debug("Compiling optical circuit intent between {} and {}", src, dst);
155
156 // Reserve OduClt ports
157 if (!deviceResourceService.requestPorts(new HashSet(Arrays.asList(srcPort, dstPort)), intent)) {
158 throw new IntentCompilationException("Unable to reserve ports for intent " + intent);
159 }
160
161 LinkedList<Intent> intents = new LinkedList<>();
162
163 FlowRuleIntent circuitIntent;
164 OpticalConnectivityIntent connIntent = findOpticalConnectivityIntent(intent);
165
166 // Create optical connectivity intent if needed
167 if (connIntent == null) {
168 // Find OCh ports with available resources
169 Pair<OchPort, OchPort> ochPorts = findPorts(intent);
170
171 if (ochPorts == null) {
172 return Collections.emptyList();
173 }
174
175 // Create optical connectivity intent
176 ConnectPoint srcCP = new ConnectPoint(src.elementId(), ochPorts.getLeft().number());
177 ConnectPoint dstCP = new ConnectPoint(dst.elementId(), ochPorts.getRight().number());
178 // FIXME: hardcoded ODU signal type
179 connIntent = OpticalConnectivityIntent.builder()
180 .appId(appId)
181 .src(srcCP)
182 .dst(dstCP)
183 .signalType(OduSignalType.ODU4)
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700184 .bidirectional(intent.isBidirectional())
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700185 .build();
Marc De Leenheer723f5532015-06-03 20:16:17 -0700186 intentService.submit(connIntent);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700187 }
188
189 // Create optical circuit intent
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700190 List<FlowRule> rules = new LinkedList<>();
Brian O'Connor81134662015-06-25 17:23:33 -0400191 rules.add(connectPorts(src, connIntent.getSrc(), intent.priority()));
192 rules.add(connectPorts(connIntent.getDst(), dst, intent.priority()));
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700193
194 // Create flow rules for reverse path
195 if (intent.isBidirectional()) {
Brian O'Connor81134662015-06-25 17:23:33 -0400196 rules.add(connectPorts(connIntent.getSrc(), src, intent.priority()));
197 rules.add(connectPorts(dst, connIntent.getDst(), intent.priority()));
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700198 }
199
200 circuitIntent = new FlowRuleIntent(appId, rules, intent.resources());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700201
202 // Save circuit to connectivity intent mapping
Marc De Leenheer723f5532015-06-03 20:16:17 -0700203 deviceResourceService.requestMapping(connIntent.id(), intent.id());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700204 intents.add(circuitIntent);
205
206 return intents;
207 }
208
209 /**
210 * Checks if current allocations on given resource can satisfy request.
Marc De Leenheer723f5532015-06-03 20:16:17 -0700211 * If the resource is null, return true.
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700212 *
Marc De Leenheerc9733082015-06-04 12:22:38 -0700213 * @param request the intent making the request
214 * @param resource the resource on which to map the intent
215 * @return true if the resource can accept the request, false otherwise
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700216 */
217 private boolean isAvailable(Intent request, IntentId resource) {
Marc De Leenheer723f5532015-06-03 20:16:17 -0700218 if (resource == null) {
219 return true;
220 }
221
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700222 Set<IntentId> mapping = deviceResourceService.getMapping(resource);
223
Marc De Leenheer723f5532015-06-03 20:16:17 -0700224 if (mapping == null) {
225 return true;
226 }
227
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700228 return mapping.size() < maxCapacity;
Marc De Leenheer723f5532015-06-03 20:16:17 -0700229 }
230
231 private boolean isAllowed(OpticalCircuitIntent circuitIntent, OpticalConnectivityIntent connIntent) {
232 ConnectPoint srcStaticPort = staticPort(circuitIntent.getSrc());
233 if (srcStaticPort != null) {
234 if (!srcStaticPort.equals(connIntent.getSrc())) {
235 return false;
236 }
237 }
238
239 ConnectPoint dstStaticPort = staticPort(circuitIntent.getDst());
240 if (dstStaticPort != null) {
241 if (!dstStaticPort.equals(connIntent.getDst())) {
242 return false;
243 }
244 }
245
246 return true;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700247 }
248
249 /**
250 * Returns existing and available optical connectivity intent that matches the given circuit intent.
251 *
252 * @param circuitIntent optical circuit intent
253 * @return existing optical connectivity intent, null otherwise.
254 */
255 private OpticalConnectivityIntent findOpticalConnectivityIntent(OpticalCircuitIntent circuitIntent) {
256 for (Intent intent : intentService.getIntents()) {
257 if (!(intent instanceof OpticalConnectivityIntent)) {
258 continue;
259 }
260
261 OpticalConnectivityIntent connIntent = (OpticalConnectivityIntent) intent;
262
263 ConnectPoint src = circuitIntent.getSrc();
264 ConnectPoint dst = circuitIntent.getDst();
Marc De Leenheer723f5532015-06-03 20:16:17 -0700265 // Ignore if the intents don't have identical src and dst devices
266 if (!src.deviceId().equals(connIntent.getSrc().deviceId()) &&
267 !dst.deviceId().equals(connIntent.getDst().deviceId())) {
268 continue;
269 }
270
271 if (!isAllowed(circuitIntent, connIntent)) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700272 continue;
273 }
274
275 if (isAvailable(circuitIntent, connIntent.id())) {
276 return connIntent;
277 }
278 }
279
280 return null;
281 }
282
Marc De Leenheer723f5532015-06-03 20:16:17 -0700283 private ConnectPoint staticPort(ConnectPoint connectPoint) {
284 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
285
286 String staticPort = port.annotations().value(AnnotationKeys.STATIC_PORT);
287
288 // FIXME: need a better way to match the port
289 if (staticPort != null) {
290 for (Port p : deviceService.getPorts(connectPoint.deviceId())) {
291 if (staticPort.equals(p.number().name())) {
292 return new ConnectPoint(p.element().id(), p.number());
293 }
294 }
295 }
296
297 return null;
298 }
299
300 private OchPort findAvailableOchPort(ConnectPoint oduPort, OpticalCircuitIntent circuitIntent) {
301 // First see if the port mappings are constrained
302 ConnectPoint ochCP = staticPort(oduPort);
303
304 if (ochCP != null) {
305 OchPort ochPort = (OchPort) deviceService.getPort(ochCP.deviceId(), ochCP.port());
306 IntentId intentId = deviceResourceService.getAllocations(ochPort);
307 if (isAvailable(circuitIntent, intentId)) {
308 return ochPort;
309 }
310 }
311
312 // No port constraints, so find any port that works
313 List<Port> ports = deviceService.getPorts(oduPort.deviceId());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700314
315 for (Port port : ports) {
316 if (!(port instanceof OchPort)) {
317 continue;
318 }
319
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700320 IntentId intentId = deviceResourceService.getAllocations(port);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700321 if (isAvailable(circuitIntent, intentId)) {
322 return (OchPort) port;
323 }
324 }
325
326 return null;
327 }
328
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700329 private Pair<OchPort, OchPort> findPorts(OpticalCircuitIntent intent) {
330
Marc De Leenheer723f5532015-06-03 20:16:17 -0700331 OchPort srcPort = findAvailableOchPort(intent.getSrc(), intent);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700332 if (srcPort == null) {
333 return null;
334 }
335
Marc De Leenheer723f5532015-06-03 20:16:17 -0700336 OchPort dstPort = findAvailableOchPort(intent.getDst(), intent);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700337 if (dstPort == null) {
338 return null;
339 }
340
341 return Pair.of(srcPort, dstPort);
342 }
343
344 /**
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700345 * Builds flow rule for mapping between two ports.
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700346 *
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700347 * @param src source port
348 * @param dst destination port
349 * @return flow rules
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700350 */
Brian O'Connor81134662015-06-25 17:23:33 -0400351 private FlowRule connectPorts(ConnectPoint src, ConnectPoint dst, int priority) {
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700352 checkArgument(src.deviceId().equals(dst.deviceId()));
353
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700354 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
355 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
356
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700357 selectorBuilder.matchInPort(src.port());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700358 //selectorBuilder.add(Criteria.matchCltSignalType)
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700359 treatmentBuilder.setOutput(dst.port());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700360 //treatmentBuilder.add(Instructions.modL1OduSignalType)
361
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700362 FlowRule flowRule = DefaultFlowRule.builder()
363 .forDevice(src.deviceId())
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700364 .withSelector(selectorBuilder.build())
365 .withTreatment(treatmentBuilder.build())
Brian O'Connor81134662015-06-25 17:23:33 -0400366 .withPriority(priority)
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700367 .fromApp(appId)
368 .makePermanent()
369 .build();
370
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700371 return flowRule;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700372 }
373}