blob: 6ae262c248179d5b7219250cfcccc5e2c03a50dd [file] [log] [blame]
Thomas Vachuskaedc944c2014-11-04 15:42:25 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Thomas Vachuskaedc944c2014-11-04 15:42:25 -08003 *
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 */
Sho SHIMIZU6c28f832015-02-20 16:12:19 -080016package org.onosproject.net.intent.impl.compiler;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080017
Sho SHIMIZU9909d172014-11-11 18:33:11 -080018import com.google.common.collect.FluentIterable;
Sho SHIMIZU56531512014-11-10 15:27:49 -080019import com.google.common.collect.ImmutableList;
Luca Pretede10c782017-01-05 17:23:08 -080020import com.google.common.collect.Lists;
Brian O'Connor023a1c72015-03-20 01:11:29 +000021import org.apache.felix.scr.annotations.Component;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080022import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
Andrey Komarov2398d962016-09-26 15:11:23 +030024import org.onlab.graph.DefaultEdgeWeigher;
25import org.onlab.graph.ScalarWeight;
26import org.onlab.graph.Weight;
Luca Pretede10c782017-01-05 17:23:08 -080027import org.onlab.util.Bandwidth;
28import org.onosproject.net.ConnectPoint;
29import org.onosproject.net.DeviceId;
helenyrwu2a674902016-07-20 09:48:04 -070030import org.onosproject.net.DisjointPath;
Brian O'Connorabafb502014-12-02 22:26:20 -080031import org.onosproject.net.ElementId;
32import org.onosproject.net.Path;
Luca Pretede10c782017-01-05 17:23:08 -080033import org.onosproject.net.device.DeviceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.net.intent.ConnectivityIntent;
35import org.onosproject.net.intent.Constraint;
36import org.onosproject.net.intent.IntentCompiler;
37import org.onosproject.net.intent.IntentExtensionService;
Luca Pretede10c782017-01-05 17:23:08 -080038import org.onosproject.net.intent.constraint.BandwidthConstraint;
jaegonkim7e876632017-01-25 06:01:49 +090039import org.onosproject.net.intent.constraint.HashedPathSelectionConstraint;
Yuta HIGUCHIe37560f2017-02-02 19:53:26 -080040import org.onosproject.net.intent.constraint.MarkerConstraint;
41import org.onosproject.net.intent.constraint.PathViabilityConstraint;
Sho SHIMIZU6c28f832015-02-20 16:12:19 -080042import org.onosproject.net.intent.impl.PathNotFoundException;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.provider.ProviderId;
Luca Pretede10c782017-01-05 17:23:08 -080044import org.onosproject.net.resource.Resource;
45import org.onosproject.net.resource.ResourceAllocation;
46import org.onosproject.net.resource.ResourceConsumer;
47import org.onosproject.net.resource.ResourceId;
48import org.onosproject.net.resource.ResourceService;
49import org.onosproject.net.resource.Resources;
Andrey Komarov2398d962016-09-26 15:11:23 +030050import org.onosproject.net.topology.LinkWeigher;
Brian O'Connorabafb502014-12-02 22:26:20 -080051import org.onosproject.net.topology.PathService;
52import org.onosproject.net.topology.TopologyEdge;
Andrey Komarov2398d962016-09-26 15:11:23 +030053import org.onosproject.net.topology.TopologyVertex;
Luca Pretede10c782017-01-05 17:23:08 -080054import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080056
Luca Pretede10c782017-01-05 17:23:08 -080057import java.util.Collection;
Sho SHIMIZU56531512014-11-10 15:27:49 -080058import java.util.Collections;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080059import java.util.Iterator;
60import java.util.List;
Luca Pretede10c782017-01-05 17:23:08 -080061import java.util.Optional;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080062import java.util.Set;
Luca Pretede10c782017-01-05 17:23:08 -080063import java.util.stream.Collectors;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080064
65/**
66 * Base class for compilers of various
Brian O'Connorabafb502014-12-02 22:26:20 -080067 * {@link org.onosproject.net.intent.ConnectivityIntent connectivity intents}.
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080068 */
Brian O'Connor023a1c72015-03-20 01:11:29 +000069@Component(immediate = true)
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080070public abstract class ConnectivityIntentCompiler<T extends ConnectivityIntent>
71 implements IntentCompiler<T> {
72
Brian O'Connorabafb502014-12-02 22:26:20 -080073 private static final ProviderId PID = new ProviderId("core", "org.onosproject.core", true);
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080074
Luca Pretede10c782017-01-05 17:23:08 -080075 private static final Logger log = LoggerFactory.getLogger(ConnectivityIntentCompiler.class);
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected DeviceService deviceService;
79
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080080 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected IntentExtensionService intentManager;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected PathService pathService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Luca Pretede10c782017-01-05 17:23:08 -080087 protected ResourceService resourceService;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080088
89 /**
90 * Returns an edge-weight capable of evaluating links on the basis of the
91 * specified constraints.
92 *
93 * @param constraints path constraints
94 * @return edge-weight function
95 */
Andrey Komarov2398d962016-09-26 15:11:23 +030096 protected LinkWeigher weigher(List<Constraint> constraints) {
97 return new ConstraintBasedLinkWeigher(constraints);
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080098 }
99
100 /**
101 * Validates the specified path against the given constraints.
102 *
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800103 * @param path path to be checked
104 * @param constraints path constraints
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800105 * @return true if the path passes all constraints
106 */
107 protected boolean checkPath(Path path, List<Constraint> constraints) {
Yuta HIGUCHI8c47efe2017-03-29 19:25:24 -0700108 if (path == null) {
109 return false;
110 }
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800111 for (Constraint constraint : constraints) {
Sho SHIMIZUb1681bd2016-02-22 12:47:50 -0800112 if (!constraint.validate(path, resourceService::isAvailable)) {
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800113 return false;
114 }
115 }
116 return true;
117 }
118
119 /**
120 * Computes a path between two ConnectPoints.
121 *
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800122 * @param intent intent on which behalf path is being computed
123 * @param one start of the path
124 * @param two end of the path
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800125 * @return Path between the two
126 * @throws PathNotFoundException if a path cannot be found
127 */
Luca Preted26ea652017-01-03 15:59:30 -0800128 @Deprecated
129 protected Path getPathOrException(ConnectivityIntent intent,
130 ElementId one, ElementId two) {
131 Path path = getPath(intent, one, two);
132 if (path == null) {
133 throw new PathNotFoundException(one, two);
134 }
135 // TODO: let's be more intelligent about this eventually
136 return path;
137 }
138
139 /**
140 * Computes a path between two ConnectPoints.
141 *
142 * @param intent intent on which behalf path is being computed
143 * @param one start of the path
144 * @param two end of the path
145 * @return Path between the two, or null if no path can be found
146 */
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800147 protected Path getPath(ConnectivityIntent intent,
148 ElementId one, ElementId two) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300149 Set<Path> paths = pathService.getPaths(one, two, weigher(intent.constraints()));
Sho SHIMIZU9909d172014-11-11 18:33:11 -0800150 final List<Constraint> constraints = intent.constraints();
151 ImmutableList<Path> filtered = FluentIterable.from(paths)
Sho SHIMIZU8b7b3b22015-09-04 16:04:50 -0700152 .filter(path -> checkPath(path, constraints))
153 .toList();
Sho SHIMIZU9909d172014-11-11 18:33:11 -0800154 if (filtered.isEmpty()) {
Luca Preted26ea652017-01-03 15:59:30 -0800155 return null;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800156 }
jaegonkim7e876632017-01-25 06:01:49 +0900157
158 if (constraints.stream().anyMatch(c -> c instanceof HashedPathSelectionConstraint)) {
159 return filtered.get(intent.hashCode() % filtered.size());
160 }
161
Sho SHIMIZU9909d172014-11-11 18:33:11 -0800162 return filtered.iterator().next();
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800163 }
164
165 /**
Daniele Moro3a6e1512017-12-22 12:14:44 +0100166 * Computes all the paths between two ConnectPoints.
167 *
168 * @param intent intent on which behalf path is being computed
169 * @param one start of the path
170 * @param two end of the path
171 * @return Paths between the two, or null if no path can be found
172 */
173 protected List<Path> getPaths(ConnectivityIntent intent,
174 ElementId one, ElementId two) {
175 Set<Path> paths = pathService.getPaths(one, two, weigher(intent.constraints()));
176 final List<Constraint> constraints = intent.constraints();
177 ImmutableList<Path> filtered = FluentIterable.from(paths)
178 .filter(path -> checkPath(path, constraints))
179 .toList();
180 if (filtered.isEmpty()) {
181 return null;
182 }
183
184 return filtered;
185 }
186
187 /**
helenyrwu2a674902016-07-20 09:48:04 -0700188 * Computes a disjoint path between two ConnectPoints.
189 *
190 * @param intent intent on which behalf path is being computed
191 * @param one start of the path
192 * @param two end of the path
193 * @return DisjointPath between the two
194 * @throws PathNotFoundException if two paths cannot be found
195 */
196 protected DisjointPath getDisjointPath(ConnectivityIntent intent,
197 ElementId one, ElementId two) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300198 Set<DisjointPath> paths = pathService.getDisjointPaths(one, two, weigher(intent.constraints()));
helenyrwu2a674902016-07-20 09:48:04 -0700199 final List<Constraint> constraints = intent.constraints();
200 ImmutableList<DisjointPath> filtered = FluentIterable.from(paths)
201 .filter(path -> checkPath(path, constraints))
Yuta HIGUCHI8c47efe2017-03-29 19:25:24 -0700202 .filter(path -> checkPath(path.backup(), constraints))
helenyrwu2a674902016-07-20 09:48:04 -0700203 .toList();
204 if (filtered.isEmpty()) {
205 throw new PathNotFoundException(one, two);
206 }
jaegonkim7e876632017-01-25 06:01:49 +0900207
208 if (constraints.stream().anyMatch(c -> c instanceof HashedPathSelectionConstraint)) {
209 return filtered.get(intent.hashCode() % filtered.size());
210 }
211
helenyrwu2a674902016-07-20 09:48:04 -0700212 return filtered.iterator().next();
213 }
214
215 /**
Luca Pretede10c782017-01-05 17:23:08 -0800216 * Allocates the bandwidth specified as intent constraint on each link
217 * composing the intent, if a bandwidth constraint is specified.
218 *
219 * @param intent the intent requesting bandwidth allocation
220 * @param connectPoints the connect points composing the intent path computed
221 */
222 protected void allocateBandwidth(ConnectivityIntent intent,
223 List<ConnectPoint> connectPoints) {
224 // Retrieve bandwidth constraint if exists
225 List<Constraint> constraints = intent.constraints();
226
227 if (constraints == null) {
228 return;
229 }
230
231 Optional<Constraint> constraint =
232 constraints.stream()
233 .filter(c -> c instanceof BandwidthConstraint)
234 .findAny();
235
236 // If there is no bandwidth constraint continue
237 if (!constraint.isPresent()) {
238 return;
239 }
240
241 BandwidthConstraint bwConstraint = (BandwidthConstraint) constraint.get();
242
243 double bw = bwConstraint.bandwidth().bps();
244
245 // If a resource group is set on the intent, the resource consumer is
246 // set equal to it. Otherwise it's set to the intent key
247 ResourceConsumer newResourceConsumer =
248 intent.resourceGroup() != null ? intent.resourceGroup() : intent.key();
249
250 // Get the list of current resource allocations
251 Collection<ResourceAllocation> resourceAllocations =
252 resourceService.getResourceAllocations(newResourceConsumer);
253
254 // Get the list of resources already allocated from resource allocations
255 List<Resource> resourcesAllocated =
256 resourcesFromAllocations(resourceAllocations);
257
258 // Get the list of resource ids for resources already allocated
259 List<ResourceId> idsResourcesAllocated = resourceIds(resourcesAllocated);
260
261 // Create the list of incoming resources requested. Exclude resources
262 // already allocated.
263 List<Resource> incomingResources =
264 resources(connectPoints, bw).stream()
265 .filter(r -> !resourcesAllocated.contains(r))
266 .collect(Collectors.toList());
267
268 if (incomingResources.isEmpty()) {
269 return;
270 }
271
272 // Create the list of resources to be added, meaning their key is not
273 // present in the resources already allocated
274 List<Resource> resourcesToAdd =
275 incomingResources.stream()
276 .filter(r -> !idsResourcesAllocated.contains(r.id()))
277 .collect(Collectors.toList());
278
279 // Resources to updated are all the new valid resources except the
280 // resources to be added
281 List<Resource> resourcesToUpdate = Lists.newArrayList(incomingResources);
282 resourcesToUpdate.removeAll(resourcesToAdd);
283
284 // If there are no resources to update skip update procedures
285 if (!resourcesToUpdate.isEmpty()) {
286 // Remove old resources that need to be updated
287 // TODO: use transaction updates when available in the resource service
288 List<ResourceAllocation> resourceAllocationsToUpdate =
289 resourceAllocations.stream()
290 .filter(rA -> resourceIds(resourcesToUpdate).contains(rA.resource().id()))
291 .collect(Collectors.toList());
292 log.debug("Releasing bandwidth for intent {}: {} bps", newResourceConsumer, resourcesToUpdate);
293 resourceService.release(resourceAllocationsToUpdate);
294
295 // Update resourcesToAdd with the list of both the new resources and
296 // the resources to update
297 resourcesToAdd.addAll(resourcesToUpdate);
298 }
299
300 // Look also for resources allocated using the intent key and -if any-
301 // remove them
302 if (intent.resourceGroup() != null) {
303 // Get the list of current resource allocations made by intent key
304 Collection<ResourceAllocation> resourceAllocationsByKey =
305 resourceService.getResourceAllocations(intent.key());
306
307 resourceService.release(Lists.newArrayList(resourceAllocationsByKey));
308 }
309
310 // Allocate resources
311 log.debug("Allocating bandwidth for intent {}: {} bps", newResourceConsumer, resourcesToAdd);
312 List<ResourceAllocation> allocations =
313 resourceService.allocate(newResourceConsumer, resourcesToAdd);
314
315 if (allocations.isEmpty()) {
316 log.debug("No resources allocated for intent {}", newResourceConsumer);
317 }
318
319 log.debug("Done allocating bandwidth for intent {}", newResourceConsumer);
320 }
321
322 /**
323 * Produces a list of resources from a list of resource allocations.
324 *
325 * @param rAs the list of resource allocations
326 * @return a list of resources retrieved from the resource allocations given
327 */
328 private static List<Resource> resourcesFromAllocations(Collection<ResourceAllocation> rAs) {
329 return rAs.stream()
330 .map(ResourceAllocation::resource)
331 .collect(Collectors.toList());
332 }
333
334 /**
335 * Creates a list of continuous bandwidth resources given a list of connect
336 * points and a bandwidth.
337 *
338 * @param cps the list of connect points
339 * @param bw the bandwidth expressed as a double
340 * @return the list of resources
341 */
342 private static List<Resource> resources(List<ConnectPoint> cps, double bw) {
343 return cps.stream()
344 // Make sure the element id is a valid device id
345 .filter(cp -> cp.elementId() instanceof DeviceId)
346 // Create a continuous resource for each CP we're going through
347 .map(cp -> Resources.continuous(cp.deviceId(), cp.port(),
348 Bandwidth.class).resource(bw))
349 .collect(Collectors.toList());
350 }
351
352 /**
353 * Returns a list of resource ids given a list of resources.
354 *
355 * @param resources the list of resources
356 * @return the list of resource ids retrieved from the resources given
357 */
358 private static List<ResourceId> resourceIds(List<Resource> resources) {
359 return resources.stream()
360 .map(Resource::id)
361 .collect(Collectors.toList());
362 }
363
364 /**
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800365 * Edge-weight capable of evaluating link cost using a set of constraints.
366 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300367 protected class ConstraintBasedLinkWeigher extends DefaultEdgeWeigher<TopologyVertex, TopologyEdge>
368 implements LinkWeigher {
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800369
370 private final List<Constraint> constraints;
371
372 /**
373 * Creates a new edge-weight function capable of evaluating links
374 * on the basis of the specified constraints.
375 *
376 * @param constraints path constraints
377 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300378 ConstraintBasedLinkWeigher(List<Constraint> constraints) {
Sho SHIMIZU56531512014-11-10 15:27:49 -0800379 if (constraints == null) {
380 this.constraints = Collections.emptyList();
381 } else {
382 this.constraints = ImmutableList.copyOf(constraints);
383 }
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800384 }
385
386 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +0300387 public Weight weight(TopologyEdge edge) {
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800388
389 // iterate over all constraints in order and return the weight of
390 // the first one with fast fail over the first failure
Yuta HIGUCHIe37560f2017-02-02 19:53:26 -0800391 Iterator<Constraint> it = constraints.stream()
392 .filter(c -> !(c instanceof MarkerConstraint))
393 .filter(c -> !(c instanceof PathViabilityConstraint))
394 .iterator();
Ray Milkey460f4022014-11-05 15:41:43 -0800395
Yuta HIGUCHIa0b0a132016-09-16 15:47:49 -0700396 if (!it.hasNext()) {
Yuta HIGUCHIfa9ee8c2017-02-02 20:00:14 -0800397 return DEFAULT_HOP_WEIGHT;
Yuta HIGUCHIa0b0a132016-09-16 15:47:49 -0700398 }
399
Sho SHIMIZUb1681bd2016-02-22 12:47:50 -0800400 double cost = it.next().cost(edge.link(), resourceService::isAvailable);
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800401 while (it.hasNext() && cost > 0) {
Sho SHIMIZUb1681bd2016-02-22 12:47:50 -0800402 if (it.next().cost(edge.link(), resourceService::isAvailable) < 0) {
Yuta HIGUCHIe37560f2017-02-02 19:53:26 -0800403 // TODO shouldn't this be non-viable?
Andrey Komarov2398d962016-09-26 15:11:23 +0300404 cost = -1;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800405 }
406 }
Yuta HIGUCHIe37560f2017-02-02 19:53:26 -0800407 return ScalarWeight.toWeight(cost);
Ray Milkey460f4022014-11-05 15:41:43 -0800408
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800409 }
410 }
411
412}