blob: 18a8f174ed68609479c47a8315305888b06b697f [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;
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +020030import org.onosproject.net.OduSignalId;
31import org.onosproject.net.OduSignalType;
32import org.onosproject.net.OduSignalUtils;
33import org.onosproject.net.OtuPort;
34import org.onosproject.net.Path;
35import org.onosproject.net.Port;
36import org.onosproject.net.TributarySlot;
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.flow.criteria.Criteria;
45import org.onosproject.net.flow.instructions.Instructions;
46import org.onosproject.net.intent.FlowRuleIntent;
47import org.onosproject.net.intent.Intent;
48import org.onosproject.net.intent.IntentCompiler;
49import org.onosproject.net.intent.IntentExtensionService;
50import org.onosproject.net.intent.OpticalOduIntent;
51import org.onosproject.net.intent.impl.IntentCompilationException;
HIGUCHI Yuta4c0ef6b2016-05-02 19:45:41 -070052import org.onosproject.net.optical.OduCltPort;
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)) {
141 throw new IntentCompilationException("Ports for the intent are not available. Intent: " + intent);
142 }
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()) {
151 throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent);
152 }
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
179 return Collections.singletonList(new FlowRuleIntent(appId,
180 rules, ImmutableSet.copyOf(path.links())));
181 }
182
183 throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent);
184 }
185
186 /**
187 * Find available TributarySlots across path.
188 *
189 * @param intent
190 * @param path path in OTU topology
191 * @return Map of Linkey and Set of available TributarySlots on its ports
192 */
193 private Map<LinkKey, Set<TributarySlot>> findAvailableTributarySlots(OpticalOduIntent intent, Path path) {
194 Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(path.links().size());
195 for (int i = 0; i < path.links().size(); i++) {
196 LinkKey link = linkKey(path.links().get(i));
197 linkRequest.add(link);
198 }
199
200 return findTributarySlots(intent, linkRequest);
201 }
202
203 private List<Resource> convertToResources(Map<LinkKey, Set<TributarySlot>> slotsMap) {
204 // Same TributarySlots are used for both directions
205 Set<Resource> resources = slotsMap.entrySet().stream()
206 .flatMap(x -> x.getValue()
207 .stream()
208 .flatMap(ts-> Stream.of(
209 Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port())
210 .resource().child(ts),
211 Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port())
212 .resource().child(ts))))
213 .collect(Collectors.toSet());
214 return (ImmutableList.copyOf(resources));
215 }
216
217 private void allocateResources(Intent intent, List<Resource> resources) {
218 // reserve all of required resources
219 List<ResourceAllocation> allocations = resourceService.allocate(intent.id(), resources);
220 if (allocations.isEmpty()) {
221 log.info("Resource allocation for {} failed (resource request: {})", intent, resources);
222 throw new IntentCompilationException("Unable to allocate resources: " + resources);
223 }
224 }
225
226 private Map<LinkKey, Set<TributarySlot>> findTributarySlots(OpticalOduIntent intent, Set<LinkKey> links) {
227 OduSignalType oduSignalType = OduSignalUtils.mappingCltSignalTypeToOduSignalType(intent.getSignalType());
228 int requestedTsNum = oduSignalType.tributarySlots();
229
230 Map<LinkKey, Set<TributarySlot>> slotsMap = new HashMap<>();
231 for (LinkKey link : links) {
232 Set<TributarySlot> common = findCommonTributarySlotsOnCps(link.src(), link.dst());
233 if (common.isEmpty() || (common.size() < requestedTsNum)) {
234 log.debug("Failed to find TributarySlots on link {} requestedTsNum={}", link, requestedTsNum);
235 return Collections.emptyMap(); // failed to find enough available TributarySlots on a link
236 }
237 slotsMap.put(link, common.stream()
238 .limit(requestedTsNum)
239 .collect(Collectors.toSet()));
240 }
241 return slotsMap;
242 }
243
244 /**
245 * Calculates optical paths in OTU topology.
246 *
247 * @param intent optical ODU intent
248 * @return set of paths in OTU topology
249 */
250 private Set<Path> getOpticalPaths(OpticalOduIntent intent) {
251 // Route in OTU topology
252 Topology topology = topologyService.currentTopology();
253
254 LinkWeight weight = edge -> {
255 // Disregard inactive or non-optical links
256 if (edge.link().state() == Link.State.INACTIVE) {
257 return -1;
258 }
259 if (edge.link().type() != Link.Type.OPTICAL) {
260 return -1;
261 }
262 // Find path with available TributarySlots resources
263 if (!isAvailableTributarySlots(intent, edge.link())) {
264 return -1;
265 }
266 return 1;
267 };
268
269 ConnectPoint start = intent.getSrc();
270 ConnectPoint end = intent.getDst();
271
272 return topologyService.getPaths(topology, start.deviceId(), end.deviceId(), weight);
273 }
274
275 private boolean isAvailableTributarySlots(OpticalOduIntent intent, Link link) {
276 OduSignalType oduSignalType = OduSignalUtils.mappingCltSignalTypeToOduSignalType(intent.getSignalType());
277 int requestedTsNum = oduSignalType.tributarySlots();
278
279 Set<TributarySlot> common = findCommonTributarySlotsOnCps(link.src(), link.dst());
280 if (common.isEmpty() || (common.size() < requestedTsNum)) {
281 log.debug("Not enough available TributarySlots on link {} requestedTsNum={}", link, requestedTsNum);
282 return false;
283 }
284 return true;
285 }
286
287 /**
288 * Create rules for the forward (or the reverse) path of the intent.
289 *
290 * @param intent OpticalOduIntent intent
291 * @param path path found for intent
292 * @param slotsMap Map of LinkKey and TributarySlots resources
293 * @return list of flow rules
294 */
295 private List<FlowRule> createRules(OpticalOduIntent intent, ConnectPoint src, ConnectPoint dst,
296 Path path, Map<LinkKey, Set<TributarySlot>> slotsMap, boolean reverse) {
297 // Build the ingress OTN rule
298 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
299 selector.matchInPort(src.port());
300 OduSignalType oduCltPortOduSignalType =
301 OduSignalUtils.mappingCltSignalTypeToOduSignalType(intent.getSignalType());
302 selector.add(Criteria.matchOduSignalType(oduCltPortOduSignalType));
303
304 List<FlowRule> rules = new LinkedList<>();
305 ConnectPoint current = src;
306
307 List<Link> links = ((!reverse) ? path.links() : Lists.reverse(path.links()));
308
309 for (Link link : links) {
310 Set<TributarySlot> slots = slotsMap.get(linkKey(link));
311 OtuPort otuPort = (OtuPort) (deviceService.getPort(link.src().deviceId(), link.src().port()));
312 OduSignalType otuPortOduSignalType =
313 OduSignalUtils.mappingOtuSignalTypeToOduSignalType(otuPort.signalType());
314
315 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
316 OduSignalId oduSignalId = null;
317 // use Instruction of OduSignalId only in case of ODU Multiplexing
318 if (oduCltPortOduSignalType != otuPortOduSignalType) {
319 oduSignalId = OduSignalUtils.buildOduSignalId(otuPortOduSignalType, slots);
320 treat.add(Instructions.modL1OduSignalId(oduSignalId));
321 }
322 ConnectPoint next = ((!reverse) ? link.src() : link.dst());
323 treat.setOutput(next.port());
324
325 FlowRule rule = createFlowRule(intent, current.deviceId(), selector.build(), treat.build());
326 rules.add(rule);
327
328 current = ((!reverse) ? link.dst() : link.src());
329 selector = DefaultTrafficSelector.builder();
330 selector.matchInPort(current.port());
331 selector.add(Criteria.matchOduSignalType(oduCltPortOduSignalType));
332 // use Criteria of OduSignalId only in case of ODU Multiplexing
333 if (oduCltPortOduSignalType != otuPortOduSignalType) {
334 selector.add(Criteria.matchOduSignalId(oduSignalId));
335 }
336 }
337
338 // Build the egress OTN rule
339 TrafficTreatment.Builder treatLast = DefaultTrafficTreatment.builder();
340 treatLast.setOutput(dst.port());
341
342 FlowRule rule = createFlowRule(intent, dst.deviceId(), selector.build(), treatLast.build());
343 rules.add(rule);
344
345 return rules;
346 }
347
348 private FlowRule createFlowRule(OpticalOduIntent intent, DeviceId deviceId,
349 TrafficSelector selector, TrafficTreatment treat) {
350 return DefaultFlowRule.builder()
351 .forDevice(deviceId)
352 .withSelector(selector)
353 .withTreatment(treat)
354 .withPriority(intent.priority())
355 .fromApp(appId)
356 .makePermanent()
357 .build();
358 }
359
360 /**
361 * Finds the common TributarySlots available on the two connect points.
362 *
363 * @param src source connect point
364 * @param dst dest connect point
365 * @return set of common TributarySlots on both connect points
366 */
367 Set<TributarySlot> findCommonTributarySlotsOnCps(ConnectPoint src, ConnectPoint dst) {
368 Set<TributarySlot> forward = findTributarySlotsOnCp(src);
369 Set<TributarySlot> backward = findTributarySlotsOnCp(dst);
370 return Sets.intersection(forward, backward);
371 }
372
373 /**
374 * Finds the TributarySlots available on the connect point.
375 *
376 * @param cp connect point
377 * @return set of TributarySlots available on the connect point
378 */
379 Set<TributarySlot> findTributarySlotsOnCp(ConnectPoint cp) {
380 return resourceService.getAvailableResourceValues(
381 Resources.discrete(cp.deviceId(), cp.port()).id(),
382 TributarySlot.class);
383 }
384}