blob: 918438c6e3c1a9057abfa97634f62b69ca265a2b [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 */
16package org.onosproject.net.intent.impl.compiler;
17
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;
30import org.onosproject.net.OduCltPort;
31import org.onosproject.net.OduSignalId;
32import org.onosproject.net.OduSignalType;
33import org.onosproject.net.OduSignalUtils;
34import org.onosproject.net.OtuPort;
35import 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;
52import org.onosproject.net.intent.impl.IntentCompilationException;
53import 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;
80
81/**
82 * An intent compiler for {@link org.onosproject.net.intent.OpticalOduIntent}.
83 */
84@Component(immediate = true)
85public class OpticalOduIntentCompiler implements IntentCompiler<OpticalOduIntent> {
86
87 private static final Logger log = LoggerFactory.getLogger(OpticalOduIntentCompiler.class);
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected IntentExtensionService intentManager;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected CoreService coreService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected TopologyService topologyService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected DeviceService deviceService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected ResourceService resourceService;
103
104 private ApplicationId appId;
105
106 @Activate
107 public void activate() {
108 appId = coreService.registerApplication("org.onosproject.net.intent");
109 intentManager.registerCompiler(OpticalOduIntent.class, this);
110 }
111
112 @Deactivate
113 public void deactivate() {
114 intentManager.unregisterCompiler(OpticalOduIntent.class);
115 }
116
117 @Override
118 public List<Intent> compile(OpticalOduIntent intent, List<Intent> installable) {
119 // Check if ports are OduClt ports
120 ConnectPoint src = intent.getSrc();
121 ConnectPoint dst = intent.getDst();
122 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
123 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
124 checkArgument(srcPort instanceof OduCltPort);
125 checkArgument(dstPort instanceof OduCltPort);
126
127 log.debug("Compiling optical ODU intent between {} and {}", src, dst);
128
129 // Release of intent resources here is only a temporary solution for handling the
130 // case of recompiling due to intent restoration (when intent state is FAILED).
131 // TODO: try to release intent resources in IntentManager.
132 resourceService.release(intent.id());
133
134 // Check OduClt ports availability
135 Resource srcPortResource = Resources.discrete(src.deviceId(), src.port()).resource();
136 Resource dstPortResource = Resources.discrete(dst.deviceId(), dst.port()).resource();
137 // If ports are not available, compilation fails
138 if (!Stream.of(srcPortResource, dstPortResource).allMatch(resourceService::isAvailable)) {
139 throw new IntentCompilationException("Ports for the intent are not available. Intent: " + intent);
140 }
141 List<Resource> intentResources = new ArrayList<>();
142 intentResources.add(srcPortResource);
143 intentResources.add(dstPortResource);
144
145 // Calculate available light paths
146 Set<Path> paths = getOpticalPaths(intent);
147
148 if (paths.isEmpty()) {
149 throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent);
150 }
151
152 // Use first path that can be successfully reserved
153 for (Path path : paths) {
154
155 // Find available Tributary Slots on both directions of path
156 Map<LinkKey, Set<TributarySlot>> slotsMap = findAvailableTributarySlots(intent, path);
157 if (slotsMap.isEmpty()) {
158 continue;
159 }
160 List<Resource> tributarySlotResources = convertToResources(slotsMap);
161 if (!tributarySlotResources.stream().allMatch(resourceService::isAvailable)) {
162 continue;
163 }
164
165 intentResources.addAll(tributarySlotResources);
166
167 allocateResources(intent, intentResources);
168
169 List<FlowRule> rules = new LinkedList<>();
170
171 // Create rules for forward and reverse path
172 rules = createRules(intent, intent.getSrc(), intent.getDst(), path, slotsMap, false);
173 if (intent.isBidirectional()) {
174 rules.addAll(createRules(intent, intent.getDst(), intent.getSrc(), path, slotsMap, true));
175 }
176
177 return Collections.singletonList(new FlowRuleIntent(appId,
178 rules, ImmutableSet.copyOf(path.links())));
179 }
180
181 throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent);
182 }
183
184 /**
185 * Find available TributarySlots across path.
186 *
187 * @param intent
188 * @param path path in OTU topology
189 * @return Map of Linkey and Set of available TributarySlots on its ports
190 */
191 private Map<LinkKey, Set<TributarySlot>> findAvailableTributarySlots(OpticalOduIntent intent, Path path) {
192 Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(path.links().size());
193 for (int i = 0; i < path.links().size(); i++) {
194 LinkKey link = linkKey(path.links().get(i));
195 linkRequest.add(link);
196 }
197
198 return findTributarySlots(intent, linkRequest);
199 }
200
201 private List<Resource> convertToResources(Map<LinkKey, Set<TributarySlot>> slotsMap) {
202 // Same TributarySlots are used for both directions
203 Set<Resource> resources = slotsMap.entrySet().stream()
204 .flatMap(x -> x.getValue()
205 .stream()
206 .flatMap(ts-> Stream.of(
207 Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port())
208 .resource().child(ts),
209 Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port())
210 .resource().child(ts))))
211 .collect(Collectors.toSet());
212 return (ImmutableList.copyOf(resources));
213 }
214
215 private void allocateResources(Intent intent, List<Resource> resources) {
216 // reserve all of required resources
217 List<ResourceAllocation> allocations = resourceService.allocate(intent.id(), resources);
218 if (allocations.isEmpty()) {
219 log.info("Resource allocation for {} failed (resource request: {})", intent, resources);
220 throw new IntentCompilationException("Unable to allocate resources: " + resources);
221 }
222 }
223
224 private Map<LinkKey, Set<TributarySlot>> findTributarySlots(OpticalOduIntent intent, Set<LinkKey> links) {
225 OduSignalType oduSignalType = OduSignalUtils.mappingCltSignalTypeToOduSignalType(intent.getSignalType());
226 int requestedTsNum = oduSignalType.tributarySlots();
227
228 Map<LinkKey, Set<TributarySlot>> slotsMap = new HashMap<>();
229 for (LinkKey link : links) {
230 Set<TributarySlot> common = findCommonTributarySlotsOnCps(link.src(), link.dst());
231 if (common.isEmpty() || (common.size() < requestedTsNum)) {
232 log.debug("Failed to find TributarySlots on link {} requestedTsNum={}", link, requestedTsNum);
233 return Collections.emptyMap(); // failed to find enough available TributarySlots on a link
234 }
235 slotsMap.put(link, common.stream()
236 .limit(requestedTsNum)
237 .collect(Collectors.toSet()));
238 }
239 return slotsMap;
240 }
241
242 /**
243 * Calculates optical paths in OTU topology.
244 *
245 * @param intent optical ODU intent
246 * @return set of paths in OTU topology
247 */
248 private Set<Path> getOpticalPaths(OpticalOduIntent intent) {
249 // Route in OTU topology
250 Topology topology = topologyService.currentTopology();
251
252 LinkWeight weight = edge -> {
253 // Disregard inactive or non-optical links
254 if (edge.link().state() == Link.State.INACTIVE) {
255 return -1;
256 }
257 if (edge.link().type() != Link.Type.OPTICAL) {
258 return -1;
259 }
260 // Find path with available TributarySlots resources
261 if (!isAvailableTributarySlots(intent, edge.link())) {
262 return -1;
263 }
264 return 1;
265 };
266
267 ConnectPoint start = intent.getSrc();
268 ConnectPoint end = intent.getDst();
269
270 return topologyService.getPaths(topology, start.deviceId(), end.deviceId(), weight);
271 }
272
273 private boolean isAvailableTributarySlots(OpticalOduIntent intent, Link link) {
274 OduSignalType oduSignalType = OduSignalUtils.mappingCltSignalTypeToOduSignalType(intent.getSignalType());
275 int requestedTsNum = oduSignalType.tributarySlots();
276
277 Set<TributarySlot> common = findCommonTributarySlotsOnCps(link.src(), link.dst());
278 if (common.isEmpty() || (common.size() < requestedTsNum)) {
279 log.debug("Not enough available TributarySlots on link {} requestedTsNum={}", link, requestedTsNum);
280 return false;
281 }
282 return true;
283 }
284
285 /**
286 * Create rules for the forward (or the reverse) path of the intent.
287 *
288 * @param intent OpticalOduIntent intent
289 * @param path path found for intent
290 * @param slotsMap Map of LinkKey and TributarySlots resources
291 * @return list of flow rules
292 */
293 private List<FlowRule> createRules(OpticalOduIntent intent, ConnectPoint src, ConnectPoint dst,
294 Path path, Map<LinkKey, Set<TributarySlot>> slotsMap, boolean reverse) {
295 // Build the ingress OTN rule
296 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
297 selector.matchInPort(src.port());
298 OduSignalType oduCltPortOduSignalType =
299 OduSignalUtils.mappingCltSignalTypeToOduSignalType(intent.getSignalType());
300 selector.add(Criteria.matchOduSignalType(oduCltPortOduSignalType));
301
302 List<FlowRule> rules = new LinkedList<>();
303 ConnectPoint current = src;
304
305 List<Link> links = ((!reverse) ? path.links() : Lists.reverse(path.links()));
306
307 for (Link link : links) {
308 Set<TributarySlot> slots = slotsMap.get(linkKey(link));
309 OtuPort otuPort = (OtuPort) (deviceService.getPort(link.src().deviceId(), link.src().port()));
310 OduSignalType otuPortOduSignalType =
311 OduSignalUtils.mappingOtuSignalTypeToOduSignalType(otuPort.signalType());
312
313 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
314 OduSignalId oduSignalId = null;
315 // use Instruction of OduSignalId only in case of ODU Multiplexing
316 if (oduCltPortOduSignalType != otuPortOduSignalType) {
317 oduSignalId = OduSignalUtils.buildOduSignalId(otuPortOduSignalType, slots);
318 treat.add(Instructions.modL1OduSignalId(oduSignalId));
319 }
320 ConnectPoint next = ((!reverse) ? link.src() : link.dst());
321 treat.setOutput(next.port());
322
323 FlowRule rule = createFlowRule(intent, current.deviceId(), selector.build(), treat.build());
324 rules.add(rule);
325
326 current = ((!reverse) ? link.dst() : link.src());
327 selector = DefaultTrafficSelector.builder();
328 selector.matchInPort(current.port());
329 selector.add(Criteria.matchOduSignalType(oduCltPortOduSignalType));
330 // use Criteria of OduSignalId only in case of ODU Multiplexing
331 if (oduCltPortOduSignalType != otuPortOduSignalType) {
332 selector.add(Criteria.matchOduSignalId(oduSignalId));
333 }
334 }
335
336 // Build the egress OTN rule
337 TrafficTreatment.Builder treatLast = DefaultTrafficTreatment.builder();
338 treatLast.setOutput(dst.port());
339
340 FlowRule rule = createFlowRule(intent, dst.deviceId(), selector.build(), treatLast.build());
341 rules.add(rule);
342
343 return rules;
344 }
345
346 private FlowRule createFlowRule(OpticalOduIntent intent, DeviceId deviceId,
347 TrafficSelector selector, TrafficTreatment treat) {
348 return DefaultFlowRule.builder()
349 .forDevice(deviceId)
350 .withSelector(selector)
351 .withTreatment(treat)
352 .withPriority(intent.priority())
353 .fromApp(appId)
354 .makePermanent()
355 .build();
356 }
357
358 /**
359 * Finds the common TributarySlots available on the two connect points.
360 *
361 * @param src source connect point
362 * @param dst dest connect point
363 * @return set of common TributarySlots on both connect points
364 */
365 Set<TributarySlot> findCommonTributarySlotsOnCps(ConnectPoint src, ConnectPoint dst) {
366 Set<TributarySlot> forward = findTributarySlotsOnCp(src);
367 Set<TributarySlot> backward = findTributarySlotsOnCp(dst);
368 return Sets.intersection(forward, backward);
369 }
370
371 /**
372 * Finds the TributarySlots available on the connect point.
373 *
374 * @param cp connect point
375 * @return set of TributarySlots available on the connect point
376 */
377 Set<TributarySlot> findTributarySlotsOnCp(ConnectPoint cp) {
378 return resourceService.getAvailableResourceValues(
379 Resources.discrete(cp.deviceId(), cp.port()).id(),
380 TributarySlot.class);
381 }
382}