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