blob: a54c92d436e9c0c99fa0b6eb6c95987fabb0b23b [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;
Sho SHIMIZU5c16df82015-09-29 12:52:07 -070052import org.onosproject.net.newresource.ResourceAllocation;
53import org.onosproject.net.newresource.ResourcePath;
54import org.onosproject.net.newresource.ResourceService;
Sho SHIMIZUa6426cb2015-10-01 15:46:48 -070055import org.onosproject.net.resource.device.DeviceResourceStore;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070056import org.onosproject.net.resource.link.LinkResourceAllocations;
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -070057import org.osgi.service.component.ComponentContext;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070058import org.slf4j.Logger;
59import org.slf4j.LoggerFactory;
60
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070061import java.util.Collections;
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -070062import java.util.Dictionary;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070063import java.util.LinkedList;
64import java.util.List;
Sho SHIMIZU5c16df82015-09-29 12:52:07 -070065import java.util.Optional;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070066import java.util.Set;
67
68import static com.google.common.base.Preconditions.checkArgument;
69
70/**
71 * An intent compiler for {@link org.onosproject.net.intent.OpticalCircuitIntent}.
72 */
73@Component(immediate = true)
74public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircuitIntent> {
75
76 private static final Logger log = LoggerFactory.getLogger(OpticalCircuitIntentCompiler.class);
77
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -070078 private static final int DEFAULT_MAX_CAPACITY = 10;
79
80 @Property(name = "maxCapacity", intValue = DEFAULT_MAX_CAPACITY,
81 label = "Maximum utilization of an optical connection.")
82
83 private int maxCapacity = DEFAULT_MAX_CAPACITY;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected ComponentConfigService cfgService;
87
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070088 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected IntentExtensionService intentManager;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected CoreService coreService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected DeviceService deviceService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Sho SHIMIZU5c16df82015-09-29 12:52:07 -070098 protected ResourceService resourceService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Sho SHIMIZUa6426cb2015-10-01 15:46:48 -0700101 protected DeviceResourceStore deviceResourceStore;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected IntentService intentService;
105
106 private ApplicationId appId;
107
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700108 @Modified
109 public void modified(ComponentContext context) {
110 Dictionary properties = context.getProperties();
111
112 //TODO for reduction check if the new capacity is smaller than the size of the current mapping
113 String propertyString = Tools.get(properties, "maxCapacity");
114
115 //Ignore if propertyString is empty
116 if (!propertyString.isEmpty()) {
117 try {
118 int temp = Integer.parseInt(propertyString);
119 //Ensure value is non-negative but allow zero as a way to shutdown the link
120 if (temp >= 0) {
121 maxCapacity = temp;
122 }
123 } catch (NumberFormatException e) {
124 //Malformed arguments lead to no change of value (user should be notified of error)
125 log.error("The value '{}' for maxCapacity was not parsable as an integer.", propertyString, e);
126 }
127 } else {
128 //Notify of empty value but do not return (other properties will also go in this function)
129 log.error("The value for maxCapacity was set to an empty value.");
130 }
131
132 }
133
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700134 @Activate
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700135 public void activate(ComponentContext context) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700136 appId = coreService.registerApplication("org.onosproject.net.intent");
137 intentManager.registerCompiler(OpticalCircuitIntent.class, this);
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700138 cfgService.registerProperties(getClass());
139 modified(context);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700140 }
141
142 @Deactivate
143 public void deactivate() {
144 intentManager.unregisterCompiler(OpticalCircuitIntent.class);
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700145 cfgService.unregisterProperties(getClass(), false);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700146 }
147
148 @Override
149 public List<Intent> compile(OpticalCircuitIntent intent, List<Intent> installable,
150 Set<LinkResourceAllocations> resources) {
151 // Check if ports are OduClt ports
152 ConnectPoint src = intent.getSrc();
153 ConnectPoint dst = intent.getDst();
154 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
155 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
156 checkArgument(srcPort instanceof OduCltPort);
157 checkArgument(dstPort instanceof OduCltPort);
158
159 log.debug("Compiling optical circuit intent between {} and {}", src, dst);
160
161 // Reserve OduClt ports
Sho SHIMIZU5c16df82015-09-29 12:52:07 -0700162 ResourcePath srcPortPath = new ResourcePath(src.deviceId(), src.port());
163 ResourcePath dstPortPath = new ResourcePath(dst.deviceId(), dst.port());
164 List<ResourceAllocation> allocation = resourceService.allocate(intent.id(), srcPortPath, dstPortPath);
165 if (allocation.isEmpty()) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700166 throw new IntentCompilationException("Unable to reserve ports for intent " + intent);
167 }
168
169 LinkedList<Intent> intents = new LinkedList<>();
170
171 FlowRuleIntent circuitIntent;
172 OpticalConnectivityIntent connIntent = findOpticalConnectivityIntent(intent);
173
174 // Create optical connectivity intent if needed
175 if (connIntent == null) {
176 // Find OCh ports with available resources
177 Pair<OchPort, OchPort> ochPorts = findPorts(intent);
178
179 if (ochPorts == null) {
180 return Collections.emptyList();
181 }
182
183 // Create optical connectivity intent
184 ConnectPoint srcCP = new ConnectPoint(src.elementId(), ochPorts.getLeft().number());
185 ConnectPoint dstCP = new ConnectPoint(dst.elementId(), ochPorts.getRight().number());
186 // FIXME: hardcoded ODU signal type
187 connIntent = OpticalConnectivityIntent.builder()
188 .appId(appId)
189 .src(srcCP)
190 .dst(dstCP)
191 .signalType(OduSignalType.ODU4)
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700192 .bidirectional(intent.isBidirectional())
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700193 .build();
Marc De Leenheer723f5532015-06-03 20:16:17 -0700194 intentService.submit(connIntent);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700195 }
196
197 // Create optical circuit intent
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700198 List<FlowRule> rules = new LinkedList<>();
Brian O'Connor81134662015-06-25 17:23:33 -0400199 rules.add(connectPorts(src, connIntent.getSrc(), intent.priority()));
200 rules.add(connectPorts(connIntent.getDst(), dst, intent.priority()));
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700201
202 // Create flow rules for reverse path
203 if (intent.isBidirectional()) {
Brian O'Connor81134662015-06-25 17:23:33 -0400204 rules.add(connectPorts(connIntent.getSrc(), src, intent.priority()));
205 rules.add(connectPorts(dst, connIntent.getDst(), intent.priority()));
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700206 }
207
208 circuitIntent = new FlowRuleIntent(appId, rules, intent.resources());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700209
210 // Save circuit to connectivity intent mapping
Sho SHIMIZUa6426cb2015-10-01 15:46:48 -0700211 deviceResourceStore.allocateMapping(connIntent.id(), intent.id());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700212 intents.add(circuitIntent);
213
214 return intents;
215 }
216
217 /**
218 * Checks if current allocations on given resource can satisfy request.
Marc De Leenheer723f5532015-06-03 20:16:17 -0700219 * If the resource is null, return true.
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700220 *
Marc De Leenheerc9733082015-06-04 12:22:38 -0700221 * @param resource the resource on which to map the intent
222 * @return true if the resource can accept the request, false otherwise
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700223 */
Sho SHIMIZU0f9a68c2015-09-29 12:45:36 -0700224 private boolean isAvailable(IntentId resource) {
Marc De Leenheer723f5532015-06-03 20:16:17 -0700225 if (resource == null) {
226 return true;
227 }
228
Sho SHIMIZUa6426cb2015-10-01 15:46:48 -0700229 Set<IntentId> mapping = deviceResourceStore.getMapping(resource);
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700230
Marc De Leenheer723f5532015-06-03 20:16:17 -0700231 if (mapping == null) {
232 return true;
233 }
234
Aaron Kruglikov6490a3b2015-06-05 14:25:32 -0700235 return mapping.size() < maxCapacity;
Marc De Leenheer723f5532015-06-03 20:16:17 -0700236 }
237
238 private boolean isAllowed(OpticalCircuitIntent circuitIntent, OpticalConnectivityIntent connIntent) {
239 ConnectPoint srcStaticPort = staticPort(circuitIntent.getSrc());
240 if (srcStaticPort != null) {
241 if (!srcStaticPort.equals(connIntent.getSrc())) {
242 return false;
243 }
244 }
245
246 ConnectPoint dstStaticPort = staticPort(circuitIntent.getDst());
247 if (dstStaticPort != null) {
248 if (!dstStaticPort.equals(connIntent.getDst())) {
249 return false;
250 }
251 }
252
253 return true;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700254 }
255
256 /**
257 * Returns existing and available optical connectivity intent that matches the given circuit intent.
258 *
259 * @param circuitIntent optical circuit intent
260 * @return existing optical connectivity intent, null otherwise.
261 */
262 private OpticalConnectivityIntent findOpticalConnectivityIntent(OpticalCircuitIntent circuitIntent) {
263 for (Intent intent : intentService.getIntents()) {
264 if (!(intent instanceof OpticalConnectivityIntent)) {
265 continue;
266 }
267
268 OpticalConnectivityIntent connIntent = (OpticalConnectivityIntent) intent;
269
270 ConnectPoint src = circuitIntent.getSrc();
271 ConnectPoint dst = circuitIntent.getDst();
Marc De Leenheer723f5532015-06-03 20:16:17 -0700272 // Ignore if the intents don't have identical src and dst devices
273 if (!src.deviceId().equals(connIntent.getSrc().deviceId()) &&
274 !dst.deviceId().equals(connIntent.getDst().deviceId())) {
275 continue;
276 }
277
278 if (!isAllowed(circuitIntent, connIntent)) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700279 continue;
280 }
281
Sho SHIMIZU0f9a68c2015-09-29 12:45:36 -0700282 if (isAvailable(connIntent.id())) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700283 return connIntent;
284 }
285 }
286
287 return null;
288 }
289
Marc De Leenheer723f5532015-06-03 20:16:17 -0700290 private ConnectPoint staticPort(ConnectPoint connectPoint) {
291 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
292
293 String staticPort = port.annotations().value(AnnotationKeys.STATIC_PORT);
294
295 // FIXME: need a better way to match the port
296 if (staticPort != null) {
297 for (Port p : deviceService.getPorts(connectPoint.deviceId())) {
298 if (staticPort.equals(p.number().name())) {
299 return new ConnectPoint(p.element().id(), p.number());
300 }
301 }
302 }
303
304 return null;
305 }
306
Sho SHIMIZUd5bdbf82015-09-29 12:46:09 -0700307 private OchPort findAvailableOchPort(ConnectPoint oduPort) {
Marc De Leenheer723f5532015-06-03 20:16:17 -0700308 // First see if the port mappings are constrained
309 ConnectPoint ochCP = staticPort(oduPort);
310
311 if (ochCP != null) {
312 OchPort ochPort = (OchPort) deviceService.getPort(ochCP.deviceId(), ochCP.port());
Sho SHIMIZU5c16df82015-09-29 12:52:07 -0700313 Optional<IntentId> intentId =
314 resourceService.getResourceAllocation(new ResourcePath(ochCP.deviceId(), ochCP.port()))
315 .map(ResourceAllocation::consumer)
316 .filter(x -> x instanceof IntentId)
317 .map(x -> (IntentId) x);
318
319 if (isAvailable(intentId.orElse(null))) {
Marc De Leenheer723f5532015-06-03 20:16:17 -0700320 return ochPort;
321 }
322 }
323
324 // No port constraints, so find any port that works
325 List<Port> ports = deviceService.getPorts(oduPort.deviceId());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700326
327 for (Port port : ports) {
328 if (!(port instanceof OchPort)) {
329 continue;
330 }
331
Sho SHIMIZU5c16df82015-09-29 12:52:07 -0700332 Optional<IntentId> intentId =
333 resourceService.getResourceAllocation(new ResourcePath(oduPort.deviceId(), port.number()))
334 .map(ResourceAllocation::consumer)
335 .filter(x -> x instanceof IntentId)
336 .map(x -> (IntentId) x);
337 if (isAvailable(intentId.orElse(null))) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700338 return (OchPort) port;
339 }
340 }
341
342 return null;
343 }
344
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700345 private Pair<OchPort, OchPort> findPorts(OpticalCircuitIntent intent) {
346
Sho SHIMIZUd5bdbf82015-09-29 12:46:09 -0700347 OchPort srcPort = findAvailableOchPort(intent.getSrc());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700348 if (srcPort == null) {
349 return null;
350 }
351
Sho SHIMIZUd5bdbf82015-09-29 12:46:09 -0700352 OchPort dstPort = findAvailableOchPort(intent.getDst());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700353 if (dstPort == null) {
354 return null;
355 }
356
357 return Pair.of(srcPort, dstPort);
358 }
359
360 /**
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700361 * Builds flow rule for mapping between two ports.
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700362 *
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700363 * @param src source port
364 * @param dst destination port
365 * @return flow rules
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700366 */
Brian O'Connor81134662015-06-25 17:23:33 -0400367 private FlowRule connectPorts(ConnectPoint src, ConnectPoint dst, int priority) {
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700368 checkArgument(src.deviceId().equals(dst.deviceId()));
369
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700370 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
371 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
372
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700373 selectorBuilder.matchInPort(src.port());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700374 //selectorBuilder.add(Criteria.matchCltSignalType)
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700375 treatmentBuilder.setOutput(dst.port());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700376 //treatmentBuilder.add(Instructions.modL1OduSignalType)
377
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700378 FlowRule flowRule = DefaultFlowRule.builder()
379 .forDevice(src.deviceId())
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700380 .withSelector(selectorBuilder.build())
381 .withTreatment(treatmentBuilder.build())
Brian O'Connor81134662015-06-25 17:23:33 -0400382 .withPriority(priority)
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700383 .fromApp(appId)
384 .makePermanent()
385 .build();
386
Marc De Leenheer4a1c1fa2015-06-01 18:08:56 -0700387 return flowRule;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700388 }
389}