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