blob: 5aa143ab0efdefc53a6298e68f0ca26b52b49be2 [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
Sho SHIMIZUac63c302015-07-02 10:19:19 -070018import com.google.common.collect.Sets;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070019import org.apache.commons.lang3.tuple.Pair;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -070023import org.apache.felix.scr.annotations.Modified;
24import org.apache.felix.scr.annotations.Property;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070025import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -070027import org.onlab.util.Tools;
28import org.onosproject.cfg.ComponentConfigService;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070029import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
Marc De Leenheer723f5532015-06-03 20:16:17 -070031import org.onosproject.net.AnnotationKeys;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070032import org.onosproject.net.ConnectPoint;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070033import org.onosproject.net.OchPort;
34import org.onosproject.net.OduCltPort;
35import org.onosproject.net.OduSignalType;
36import org.onosproject.net.Port;
37import org.onosproject.net.device.DeviceService;
38import org.onosproject.net.flow.DefaultFlowRule;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.FlowRule;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.TrafficTreatment;
44import org.onosproject.net.intent.FlowRuleIntent;
45import org.onosproject.net.intent.Intent;
46import org.onosproject.net.intent.IntentCompiler;
47import org.onosproject.net.intent.IntentExtensionService;
48import org.onosproject.net.intent.IntentId;
49import org.onosproject.net.intent.IntentService;
50import org.onosproject.net.intent.OpticalCircuitIntent;
51import org.onosproject.net.intent.OpticalConnectivityIntent;
52import org.onosproject.net.intent.impl.IntentCompilationException;
53import org.onosproject.net.resource.device.DeviceResourceService;
54import org.onosproject.net.resource.link.LinkResourceAllocations;
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -070055import org.osgi.service.component.ComponentContext;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070056import org.slf4j.Logger;
57import org.slf4j.LoggerFactory;
58
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070059import 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.LinkedList;
62import java.util.List;
63import java.util.Set;
64
65import 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)
71public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircuitIntent> {
72
73 private static final Logger log = LoggerFactory.getLogger(OpticalCircuitIntentCompiler.class);
74
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -070075 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
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070085 @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
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700102 @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
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700128 @Activate
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700129 public void activate(ComponentContext context) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700130 appId = coreService.registerApplication("org.onosproject.net.intent");
131 intentManager.registerCompiler(OpticalCircuitIntent.class, this);
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700132 cfgService.registerProperties(getClass());
133 modified(context);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700134 }
135
136 @Deactivate
137 public void deactivate() {
138 intentManager.unregisterCompiler(OpticalCircuitIntent.class);
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700139 cfgService.unregisterProperties(getClass(), false);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700140 }
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
Sho SHIMIZUac63c302015-07-02 10:19:19 -0700156 if (!deviceResourceService.requestPorts(Sets.newHashSet(srcPort, dstPort), intent)) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700157 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)
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700183 .bidirectional(intent.isBidirectional())
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700184 .build();
Marc De Leenheer723f5532015-06-03 20:16:17 -0700185 intentService.submit(connIntent);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700186 }
187
188 // Create optical circuit intent
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700189 List<FlowRule> rules = new LinkedList<>();
Brian O'Connor81134662015-06-25 17:23:33 -0400190 rules.add(connectPorts(src, connIntent.getSrc(), intent.priority()));
191 rules.add(connectPorts(connIntent.getDst(), dst, intent.priority()));
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700192
193 // Create flow rules for reverse path
194 if (intent.isBidirectional()) {
Brian O'Connor81134662015-06-25 17:23:33 -0400195 rules.add(connectPorts(connIntent.getSrc(), src, intent.priority()));
196 rules.add(connectPorts(dst, connIntent.getDst(), intent.priority()));
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700197 }
198
199 circuitIntent = new FlowRuleIntent(appId, rules, intent.resources());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700200
201 // Save circuit to connectivity intent mapping
Marc De Leenheer723f5532015-06-03 20:16:17 -0700202 deviceResourceService.requestMapping(connIntent.id(), intent.id());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700203 intents.add(circuitIntent);
204
205 return intents;
206 }
207
208 /**
209 * Checks if current allocations on given resource can satisfy request.
Marc De Leenheer723f5532015-06-03 20:16:17 -0700210 * If the resource is null, return true.
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700211 *
Marc De Leenheerc9733082015-06-04 12:22:38 -0700212 * @param resource the resource on which to map the intent
213 * @return true if the resource can accept the request, false otherwise
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700214 */
Sho SHIMIZU0f9a68c2015-09-29 12:45:36 -0700215 private boolean isAvailable(IntentId resource) {
Marc De Leenheer723f5532015-06-03 20:16:17 -0700216 if (resource == null) {
217 return true;
218 }
219
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700220 Set<IntentId> mapping = deviceResourceService.getMapping(resource);
221
Marc De Leenheer723f5532015-06-03 20:16:17 -0700222 if (mapping == null) {
223 return true;
224 }
225
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700226 return mapping.size() < maxCapacity;
Marc De Leenheer723f5532015-06-03 20:16:17 -0700227 }
228
229 private boolean isAllowed(OpticalCircuitIntent circuitIntent, OpticalConnectivityIntent connIntent) {
230 ConnectPoint srcStaticPort = staticPort(circuitIntent.getSrc());
231 if (srcStaticPort != null) {
232 if (!srcStaticPort.equals(connIntent.getSrc())) {
233 return false;
234 }
235 }
236
237 ConnectPoint dstStaticPort = staticPort(circuitIntent.getDst());
238 if (dstStaticPort != null) {
239 if (!dstStaticPort.equals(connIntent.getDst())) {
240 return false;
241 }
242 }
243
244 return true;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700245 }
246
247 /**
248 * Returns existing and available optical connectivity intent that matches the given circuit intent.
249 *
250 * @param circuitIntent optical circuit intent
251 * @return existing optical connectivity intent, null otherwise.
252 */
253 private OpticalConnectivityIntent findOpticalConnectivityIntent(OpticalCircuitIntent circuitIntent) {
254 for (Intent intent : intentService.getIntents()) {
255 if (!(intent instanceof OpticalConnectivityIntent)) {
256 continue;
257 }
258
259 OpticalConnectivityIntent connIntent = (OpticalConnectivityIntent) intent;
260
261 ConnectPoint src = circuitIntent.getSrc();
262 ConnectPoint dst = circuitIntent.getDst();
Marc De Leenheer723f5532015-06-03 20:16:17 -0700263 // Ignore if the intents don't have identical src and dst devices
264 if (!src.deviceId().equals(connIntent.getSrc().deviceId()) &&
265 !dst.deviceId().equals(connIntent.getDst().deviceId())) {
266 continue;
267 }
268
269 if (!isAllowed(circuitIntent, connIntent)) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700270 continue;
271 }
272
Sho SHIMIZU0f9a68c2015-09-29 12:45:36 -0700273 if (isAvailable(connIntent.id())) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700274 return connIntent;
275 }
276 }
277
278 return null;
279 }
280
Marc De Leenheer723f5532015-06-03 20:16:17 -0700281 private ConnectPoint staticPort(ConnectPoint connectPoint) {
282 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
283
284 String staticPort = port.annotations().value(AnnotationKeys.STATIC_PORT);
285
286 // FIXME: need a better way to match the port
287 if (staticPort != null) {
288 for (Port p : deviceService.getPorts(connectPoint.deviceId())) {
289 if (staticPort.equals(p.number().name())) {
290 return new ConnectPoint(p.element().id(), p.number());
291 }
292 }
293 }
294
295 return null;
296 }
297
Sho SHIMIZUd5bdbf82015-09-29 12:46:09 -0700298 private OchPort findAvailableOchPort(ConnectPoint oduPort) {
Marc De Leenheer723f5532015-06-03 20:16:17 -0700299 // First see if the port mappings are constrained
300 ConnectPoint ochCP = staticPort(oduPort);
301
302 if (ochCP != null) {
303 OchPort ochPort = (OchPort) deviceService.getPort(ochCP.deviceId(), ochCP.port());
304 IntentId intentId = deviceResourceService.getAllocations(ochPort);
Sho SHIMIZU0f9a68c2015-09-29 12:45:36 -0700305 if (isAvailable(intentId)) {
Marc De Leenheer723f5532015-06-03 20:16:17 -0700306 return ochPort;
307 }
308 }
309
310 // No port constraints, so find any port that works
311 List<Port> ports = deviceService.getPorts(oduPort.deviceId());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700312
313 for (Port port : ports) {
314 if (!(port instanceof OchPort)) {
315 continue;
316 }
317
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700318 IntentId intentId = deviceResourceService.getAllocations(port);
Sho SHIMIZU0f9a68c2015-09-29 12:45:36 -0700319 if (isAvailable(intentId)) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700320 return (OchPort) port;
321 }
322 }
323
324 return null;
325 }
326
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700327 private Pair<OchPort, OchPort> findPorts(OpticalCircuitIntent intent) {
328
Sho SHIMIZUd5bdbf82015-09-29 12:46:09 -0700329 OchPort srcPort = findAvailableOchPort(intent.getSrc());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700330 if (srcPort == null) {
331 return null;
332 }
333
Sho SHIMIZUd5bdbf82015-09-29 12:46:09 -0700334 OchPort dstPort = findAvailableOchPort(intent.getDst());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700335 if (dstPort == null) {
336 return null;
337 }
338
339 return Pair.of(srcPort, dstPort);
340 }
341
342 /**
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700343 * Builds flow rule for mapping between two ports.
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700344 *
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700345 * @param src source port
346 * @param dst destination port
347 * @return flow rules
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700348 */
Brian O'Connor81134662015-06-25 17:23:33 -0400349 private FlowRule connectPorts(ConnectPoint src, ConnectPoint dst, int priority) {
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700350 checkArgument(src.deviceId().equals(dst.deviceId()));
351
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700352 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
353 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
354
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700355 selectorBuilder.matchInPort(src.port());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700356 //selectorBuilder.add(Criteria.matchCltSignalType)
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700357 treatmentBuilder.setOutput(dst.port());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700358 //treatmentBuilder.add(Instructions.modL1OduSignalType)
359
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700360 FlowRule flowRule = DefaultFlowRule.builder()
361 .forDevice(src.deviceId())
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700362 .withSelector(selectorBuilder.build())
363 .withTreatment(treatmentBuilder.build())
Brian O'Connor81134662015-06-25 17:23:33 -0400364 .withPriority(priority)
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700365 .fromApp(appId)
366 .makePermanent()
367 .build();
368
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700369 return flowRule;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700370 }
371}