blob: b7222853c23b586cc65788deb72c81f849955501 [file] [log] [blame]
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +02001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +02003 *
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 */
Yuta HIGUCHId95d5902016-06-27 00:18:45 -070016package org.onosproject.net.optical.intent.impl.compiler;
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +020017
18
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.onosproject.core.ApplicationId;
25import org.onosproject.core.CoreService;
26import org.onosproject.net.ConnectPoint;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.Link;
29import org.onosproject.net.LinkKey;
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +020030import org.onosproject.net.OduSignalId;
31import org.onosproject.net.OduSignalType;
32import org.onosproject.net.OduSignalUtils;
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +020033import org.onosproject.net.Path;
34import org.onosproject.net.Port;
35import org.onosproject.net.TributarySlot;
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.flow.criteria.Criteria;
44import org.onosproject.net.flow.instructions.Instructions;
45import org.onosproject.net.intent.FlowRuleIntent;
46import org.onosproject.net.intent.Intent;
47import org.onosproject.net.intent.IntentCompiler;
48import org.onosproject.net.intent.IntentExtensionService;
49import org.onosproject.net.intent.OpticalOduIntent;
Luca Prete670ac5d2017-02-03 15:55:43 -080050import org.onosproject.net.intent.PathIntent;
HIGUCHI Yuta4c0ef6b2016-05-02 19:45:41 -070051import org.onosproject.net.optical.OduCltPort;
HIGUCHI Yuta5be3e822016-05-03 13:51:42 -070052import org.onosproject.net.optical.OtuPort;
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +020053import org.onosproject.net.resource.Resource;
54import org.onosproject.net.resource.ResourceService;
55import org.onosproject.net.resource.ResourceAllocation;
56import org.onosproject.net.resource.Resources;
57import org.onosproject.net.topology.LinkWeight;
58import org.onosproject.net.topology.Topology;
59import org.onosproject.net.topology.TopologyService;
60import org.slf4j.Logger;
61import org.slf4j.LoggerFactory;
62
63import com.google.common.collect.ImmutableList;
64import com.google.common.collect.ImmutableSet;
65import com.google.common.collect.Lists;
66import com.google.common.collect.Sets;
67
68import java.util.ArrayList;
69import java.util.Collections;
70import java.util.HashMap;
71import java.util.LinkedList;
72import java.util.List;
73import java.util.Map;
74import java.util.Set;
75import java.util.stream.Collectors;
76import java.util.stream.Stream;
77
78import static com.google.common.base.Preconditions.checkArgument;
79import static org.onosproject.net.LinkKey.linkKey;
HIGUCHI Yuta4c0ef6b2016-05-02 19:45:41 -070080import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView;
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +020081
82/**
83 * An intent compiler for {@link org.onosproject.net.intent.OpticalOduIntent}.
84 */
85@Component(immediate = true)
86public class OpticalOduIntentCompiler implements IntentCompiler<OpticalOduIntent> {
87
88 private static final Logger log = LoggerFactory.getLogger(OpticalOduIntentCompiler.class);
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected IntentExtensionService intentManager;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected CoreService coreService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected TopologyService topologyService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected DeviceService deviceService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected ResourceService resourceService;
104
105 private ApplicationId appId;
106
107 @Activate
108 public void activate() {
HIGUCHI Yuta4c0ef6b2016-05-02 19:45:41 -0700109 deviceService = opticalView(deviceService);
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +0200110 appId = coreService.registerApplication("org.onosproject.net.intent");
111 intentManager.registerCompiler(OpticalOduIntent.class, this);
112 }
113
114 @Deactivate
115 public void deactivate() {
116 intentManager.unregisterCompiler(OpticalOduIntent.class);
117 }
118
119 @Override
120 public List<Intent> compile(OpticalOduIntent intent, List<Intent> installable) {
121 // Check if ports are OduClt ports
122 ConnectPoint src = intent.getSrc();
123 ConnectPoint dst = intent.getDst();
124 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
125 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
126 checkArgument(srcPort instanceof OduCltPort);
127 checkArgument(dstPort instanceof OduCltPort);
128
129 log.debug("Compiling optical ODU intent between {} and {}", src, dst);
130
131 // Release of intent resources here is only a temporary solution for handling the
132 // case of recompiling due to intent restoration (when intent state is FAILED).
133 // TODO: try to release intent resources in IntentManager.
134 resourceService.release(intent.id());
135
136 // Check OduClt ports availability
137 Resource srcPortResource = Resources.discrete(src.deviceId(), src.port()).resource();
138 Resource dstPortResource = Resources.discrete(dst.deviceId(), dst.port()).resource();
139 // If ports are not available, compilation fails
140 if (!Stream.of(srcPortResource, dstPortResource).allMatch(resourceService::isAvailable)) {
Yuta HIGUCHId95d5902016-06-27 00:18:45 -0700141 throw new OpticalIntentCompilationException("Ports for the intent are not available. Intent: " + intent);
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +0200142 }
143 List<Resource> intentResources = new ArrayList<>();
144 intentResources.add(srcPortResource);
145 intentResources.add(dstPortResource);
146
147 // Calculate available light paths
148 Set<Path> paths = getOpticalPaths(intent);
149
150 if (paths.isEmpty()) {
Yuta HIGUCHId95d5902016-06-27 00:18:45 -0700151 throw new OpticalIntentCompilationException("Unable to find suitable lightpath for intent " + intent);
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +0200152 }
153
154 // Use first path that can be successfully reserved
155 for (Path path : paths) {
156
157 // Find available Tributary Slots on both directions of path
158 Map<LinkKey, Set<TributarySlot>> slotsMap = findAvailableTributarySlots(intent, path);
159 if (slotsMap.isEmpty()) {
160 continue;
161 }
162 List<Resource> tributarySlotResources = convertToResources(slotsMap);
163 if (!tributarySlotResources.stream().allMatch(resourceService::isAvailable)) {
164 continue;
165 }
166
167 intentResources.addAll(tributarySlotResources);
168
169 allocateResources(intent, intentResources);
170
171 List<FlowRule> rules = new LinkedList<>();
172
173 // Create rules for forward and reverse path
174 rules = createRules(intent, intent.getSrc(), intent.getDst(), path, slotsMap, false);
175 if (intent.isBidirectional()) {
176 rules.addAll(createRules(intent, intent.getDst(), intent.getSrc(), path, slotsMap, true));
177 }
178
Luca Prete670ac5d2017-02-03 15:55:43 -0800179 return Collections.singletonList(
180 new FlowRuleIntent(appId,
181 intent.key(),
182 rules,
183 ImmutableSet.copyOf(path.links()),
184 PathIntent.ProtectionType.PRIMARY,
185 intent.resourceGroup()));
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +0200186 }
187
Yuta HIGUCHId95d5902016-06-27 00:18:45 -0700188 throw new OpticalIntentCompilationException("Unable to find suitable lightpath for intent " + intent);
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +0200189 }
190
191 /**
192 * Find available TributarySlots across path.
193 *
194 * @param intent
195 * @param path path in OTU topology
196 * @return Map of Linkey and Set of available TributarySlots on its ports
197 */
198 private Map<LinkKey, Set<TributarySlot>> findAvailableTributarySlots(OpticalOduIntent intent, Path path) {
199 Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(path.links().size());
200 for (int i = 0; i < path.links().size(); i++) {
201 LinkKey link = linkKey(path.links().get(i));
202 linkRequest.add(link);
203 }
204
205 return findTributarySlots(intent, linkRequest);
206 }
207
208 private List<Resource> convertToResources(Map<LinkKey, Set<TributarySlot>> slotsMap) {
209 // Same TributarySlots are used for both directions
210 Set<Resource> resources = slotsMap.entrySet().stream()
211 .flatMap(x -> x.getValue()
212 .stream()
Ray Milkey88cc3432017-03-30 17:19:08 -0700213 .flatMap(ts -> Stream.of(
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +0200214 Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port())
215 .resource().child(ts),
216 Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port())
217 .resource().child(ts))))
218 .collect(Collectors.toSet());
219 return (ImmutableList.copyOf(resources));
220 }
221
222 private void allocateResources(Intent intent, List<Resource> resources) {
223 // reserve all of required resources
224 List<ResourceAllocation> allocations = resourceService.allocate(intent.id(), resources);
225 if (allocations.isEmpty()) {
226 log.info("Resource allocation for {} failed (resource request: {})", intent, resources);
Yuta HIGUCHId95d5902016-06-27 00:18:45 -0700227 throw new OpticalIntentCompilationException("Unable to allocate resources: " + resources);
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +0200228 }
229 }
230
231 private Map<LinkKey, Set<TributarySlot>> findTributarySlots(OpticalOduIntent intent, Set<LinkKey> links) {
232 OduSignalType oduSignalType = OduSignalUtils.mappingCltSignalTypeToOduSignalType(intent.getSignalType());
233 int requestedTsNum = oduSignalType.tributarySlots();
234
235 Map<LinkKey, Set<TributarySlot>> slotsMap = new HashMap<>();
236 for (LinkKey link : links) {
237 Set<TributarySlot> common = findCommonTributarySlotsOnCps(link.src(), link.dst());
238 if (common.isEmpty() || (common.size() < requestedTsNum)) {
239 log.debug("Failed to find TributarySlots on link {} requestedTsNum={}", link, requestedTsNum);
240 return Collections.emptyMap(); // failed to find enough available TributarySlots on a link
241 }
242 slotsMap.put(link, common.stream()
243 .limit(requestedTsNum)
244 .collect(Collectors.toSet()));
245 }
246 return slotsMap;
247 }
248
249 /**
250 * Calculates optical paths in OTU topology.
251 *
252 * @param intent optical ODU intent
253 * @return set of paths in OTU topology
254 */
255 private Set<Path> getOpticalPaths(OpticalOduIntent intent) {
256 // Route in OTU topology
257 Topology topology = topologyService.currentTopology();
258
259 LinkWeight weight = edge -> {
260 // Disregard inactive or non-optical links
261 if (edge.link().state() == Link.State.INACTIVE) {
262 return -1;
263 }
264 if (edge.link().type() != Link.Type.OPTICAL) {
265 return -1;
266 }
267 // Find path with available TributarySlots resources
268 if (!isAvailableTributarySlots(intent, edge.link())) {
269 return -1;
270 }
271 return 1;
272 };
273
274 ConnectPoint start = intent.getSrc();
275 ConnectPoint end = intent.getDst();
276
277 return topologyService.getPaths(topology, start.deviceId(), end.deviceId(), weight);
278 }
279
280 private boolean isAvailableTributarySlots(OpticalOduIntent intent, Link link) {
281 OduSignalType oduSignalType = OduSignalUtils.mappingCltSignalTypeToOduSignalType(intent.getSignalType());
282 int requestedTsNum = oduSignalType.tributarySlots();
283
284 Set<TributarySlot> common = findCommonTributarySlotsOnCps(link.src(), link.dst());
285 if (common.isEmpty() || (common.size() < requestedTsNum)) {
286 log.debug("Not enough available TributarySlots on link {} requestedTsNum={}", link, requestedTsNum);
287 return false;
288 }
289 return true;
290 }
291
292 /**
293 * Create rules for the forward (or the reverse) path of the intent.
294 *
295 * @param intent OpticalOduIntent intent
296 * @param path path found for intent
297 * @param slotsMap Map of LinkKey and TributarySlots resources
298 * @return list of flow rules
299 */
300 private List<FlowRule> createRules(OpticalOduIntent intent, ConnectPoint src, ConnectPoint dst,
301 Path path, Map<LinkKey, Set<TributarySlot>> slotsMap, boolean reverse) {
302 // Build the ingress OTN rule
303 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
304 selector.matchInPort(src.port());
305 OduSignalType oduCltPortOduSignalType =
306 OduSignalUtils.mappingCltSignalTypeToOduSignalType(intent.getSignalType());
307 selector.add(Criteria.matchOduSignalType(oduCltPortOduSignalType));
308
309 List<FlowRule> rules = new LinkedList<>();
310 ConnectPoint current = src;
311
312 List<Link> links = ((!reverse) ? path.links() : Lists.reverse(path.links()));
313
314 for (Link link : links) {
315 Set<TributarySlot> slots = slotsMap.get(linkKey(link));
316 OtuPort otuPort = (OtuPort) (deviceService.getPort(link.src().deviceId(), link.src().port()));
317 OduSignalType otuPortOduSignalType =
318 OduSignalUtils.mappingOtuSignalTypeToOduSignalType(otuPort.signalType());
319
320 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
321 OduSignalId oduSignalId = null;
322 // use Instruction of OduSignalId only in case of ODU Multiplexing
323 if (oduCltPortOduSignalType != otuPortOduSignalType) {
324 oduSignalId = OduSignalUtils.buildOduSignalId(otuPortOduSignalType, slots);
325 treat.add(Instructions.modL1OduSignalId(oduSignalId));
326 }
327 ConnectPoint next = ((!reverse) ? link.src() : link.dst());
328 treat.setOutput(next.port());
329
330 FlowRule rule = createFlowRule(intent, current.deviceId(), selector.build(), treat.build());
331 rules.add(rule);
332
333 current = ((!reverse) ? link.dst() : link.src());
334 selector = DefaultTrafficSelector.builder();
335 selector.matchInPort(current.port());
336 selector.add(Criteria.matchOduSignalType(oduCltPortOduSignalType));
337 // use Criteria of OduSignalId only in case of ODU Multiplexing
338 if (oduCltPortOduSignalType != otuPortOduSignalType) {
339 selector.add(Criteria.matchOduSignalId(oduSignalId));
340 }
341 }
342
343 // Build the egress OTN rule
344 TrafficTreatment.Builder treatLast = DefaultTrafficTreatment.builder();
345 treatLast.setOutput(dst.port());
346
347 FlowRule rule = createFlowRule(intent, dst.deviceId(), selector.build(), treatLast.build());
348 rules.add(rule);
349
350 return rules;
351 }
352
353 private FlowRule createFlowRule(OpticalOduIntent intent, DeviceId deviceId,
354 TrafficSelector selector, TrafficTreatment treat) {
355 return DefaultFlowRule.builder()
356 .forDevice(deviceId)
357 .withSelector(selector)
358 .withTreatment(treat)
359 .withPriority(intent.priority())
360 .fromApp(appId)
361 .makePermanent()
362 .build();
363 }
364
365 /**
366 * Finds the common TributarySlots available on the two connect points.
367 *
368 * @param src source connect point
369 * @param dst dest connect point
370 * @return set of common TributarySlots on both connect points
371 */
372 Set<TributarySlot> findCommonTributarySlotsOnCps(ConnectPoint src, ConnectPoint dst) {
373 Set<TributarySlot> forward = findTributarySlotsOnCp(src);
374 Set<TributarySlot> backward = findTributarySlotsOnCp(dst);
375 return Sets.intersection(forward, backward);
376 }
377
378 /**
379 * Finds the TributarySlots available on the connect point.
380 *
381 * @param cp connect point
382 * @return set of TributarySlots available on the connect point
383 */
384 Set<TributarySlot> findTributarySlotsOnCp(ConnectPoint cp) {
385 return resourceService.getAvailableResourceValues(
386 Resources.discrete(cp.deviceId(), cp.port()).id(),
387 TributarySlot.class);
388 }
Ray Milkey88cc3432017-03-30 17:19:08 -0700389}