blob: 666128c53ac6c32fd467a8861aa8f9b355cde044 [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;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070021import org.osgi.service.component.annotations.Reference;
22import org.osgi.service.component.annotations.ReferenceCardinality;
Andrey Komarov2398d962016-09-26 15:11:23 +030023import org.onlab.graph.DefaultEdgeWeigher;
24import org.onlab.graph.ScalarWeight;
25import org.onlab.graph.Weight;
Luca Pretede10c782017-01-05 17:23:08 -080026import org.onlab.util.Bandwidth;
27import org.onosproject.net.ConnectPoint;
28import org.onosproject.net.DeviceId;
helenyrwu2a674902016-07-20 09:48:04 -070029import org.onosproject.net.DisjointPath;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.net.ElementId;
31import org.onosproject.net.Path;
Luca Pretede10c782017-01-05 17:23:08 -080032import org.onosproject.net.device.DeviceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.net.intent.ConnectivityIntent;
34import org.onosproject.net.intent.Constraint;
35import org.onosproject.net.intent.IntentCompiler;
36import org.onosproject.net.intent.IntentExtensionService;
Luca Pretede10c782017-01-05 17:23:08 -080037import org.onosproject.net.intent.constraint.BandwidthConstraint;
jaegonkim7e876632017-01-25 06:01:49 +090038import org.onosproject.net.intent.constraint.HashedPathSelectionConstraint;
Yuta HIGUCHIe37560f2017-02-02 19:53:26 -080039import org.onosproject.net.intent.constraint.MarkerConstraint;
40import org.onosproject.net.intent.constraint.PathViabilityConstraint;
Sho SHIMIZU6c28f832015-02-20 16:12:19 -080041import org.onosproject.net.intent.impl.PathNotFoundException;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.net.provider.ProviderId;
Luca Pretede10c782017-01-05 17:23:08 -080043import org.onosproject.net.resource.Resource;
44import org.onosproject.net.resource.ResourceAllocation;
45import org.onosproject.net.resource.ResourceConsumer;
46import org.onosproject.net.resource.ResourceId;
47import org.onosproject.net.resource.ResourceService;
48import org.onosproject.net.resource.Resources;
Andrey Komarov2398d962016-09-26 15:11:23 +030049import org.onosproject.net.topology.LinkWeigher;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.topology.PathService;
51import org.onosproject.net.topology.TopologyEdge;
Andrey Komarov2398d962016-09-26 15:11:23 +030052import org.onosproject.net.topology.TopologyVertex;
Luca Pretede10c782017-01-05 17:23:08 -080053import org.slf4j.Logger;
54import org.slf4j.LoggerFactory;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080055
Luca Pretede10c782017-01-05 17:23:08 -080056import java.util.Collection;
Sho SHIMIZU56531512014-11-10 15:27:49 -080057import java.util.Collections;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080058import java.util.Iterator;
59import java.util.List;
Luca Pretede10c782017-01-05 17:23:08 -080060import java.util.Optional;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080061import java.util.Set;
Luca Pretede10c782017-01-05 17:23:08 -080062import java.util.stream.Collectors;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080063
64/**
65 * Base class for compilers of various
Brian O'Connorabafb502014-12-02 22:26:20 -080066 * {@link org.onosproject.net.intent.ConnectivityIntent connectivity intents}.
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080067 */
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080068public abstract class ConnectivityIntentCompiler<T extends ConnectivityIntent>
69 implements IntentCompiler<T> {
70
Brian O'Connorabafb502014-12-02 22:26:20 -080071 private static final ProviderId PID = new ProviderId("core", "org.onosproject.core", true);
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080072
Luca Pretede10c782017-01-05 17:23:08 -080073 private static final Logger log = LoggerFactory.getLogger(ConnectivityIntentCompiler.class);
74
Ray Milkeyd84f89b2018-08-17 14:54:17 -070075 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Luca Pretede10c782017-01-05 17:23:08 -080076 protected DeviceService deviceService;
77
Ray Milkeyd84f89b2018-08-17 14:54:17 -070078 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080079 protected IntentExtensionService intentManager;
80
Ray Milkeyd84f89b2018-08-17 14:54:17 -070081 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080082 protected PathService pathService;
83
Ray Milkeyd84f89b2018-08-17 14:54:17 -070084 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Luca Pretede10c782017-01-05 17:23:08 -080085 protected ResourceService resourceService;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080086
87 /**
88 * Returns an edge-weight capable of evaluating links on the basis of the
89 * specified constraints.
90 *
91 * @param constraints path constraints
92 * @return edge-weight function
93 */
Andrey Komarov2398d962016-09-26 15:11:23 +030094 protected LinkWeigher weigher(List<Constraint> constraints) {
95 return new ConstraintBasedLinkWeigher(constraints);
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080096 }
97
98 /**
99 * Validates the specified path against the given constraints.
100 *
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800101 * @param path path to be checked
102 * @param constraints path constraints
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800103 * @return true if the path passes all constraints
104 */
105 protected boolean checkPath(Path path, List<Constraint> constraints) {
Yuta HIGUCHI8c47efe2017-03-29 19:25:24 -0700106 if (path == null) {
107 return false;
108 }
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800109 for (Constraint constraint : constraints) {
Sho SHIMIZUb1681bd2016-02-22 12:47:50 -0800110 if (!constraint.validate(path, resourceService::isAvailable)) {
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800111 return false;
112 }
113 }
114 return true;
115 }
116
117 /**
118 * Computes a path between two ConnectPoints.
119 *
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800120 * @param intent intent on which behalf path is being computed
121 * @param one start of the path
122 * @param two end of the path
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800123 * @return Path between the two
124 * @throws PathNotFoundException if a path cannot be found
125 */
Luca Preted26ea652017-01-03 15:59:30 -0800126 @Deprecated
127 protected Path getPathOrException(ConnectivityIntent intent,
128 ElementId one, ElementId two) {
129 Path path = getPath(intent, one, two);
130 if (path == null) {
131 throw new PathNotFoundException(one, two);
132 }
133 // TODO: let's be more intelligent about this eventually
134 return path;
135 }
136
137 /**
138 * Computes a path between two ConnectPoints.
139 *
140 * @param intent intent on which behalf path is being computed
141 * @param one start of the path
142 * @param two end of the path
143 * @return Path between the two, or null if no path can be found
144 */
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800145 protected Path getPath(ConnectivityIntent intent,
146 ElementId one, ElementId two) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300147 Set<Path> paths = pathService.getPaths(one, two, weigher(intent.constraints()));
Sho SHIMIZU9909d172014-11-11 18:33:11 -0800148 final List<Constraint> constraints = intent.constraints();
149 ImmutableList<Path> filtered = FluentIterable.from(paths)
Sho SHIMIZU8b7b3b22015-09-04 16:04:50 -0700150 .filter(path -> checkPath(path, constraints))
151 .toList();
Sho SHIMIZU9909d172014-11-11 18:33:11 -0800152 if (filtered.isEmpty()) {
Luca Preted26ea652017-01-03 15:59:30 -0800153 return null;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800154 }
jaegonkim7e876632017-01-25 06:01:49 +0900155
156 if (constraints.stream().anyMatch(c -> c instanceof HashedPathSelectionConstraint)) {
157 return filtered.get(intent.hashCode() % filtered.size());
158 }
159
Sho SHIMIZU9909d172014-11-11 18:33:11 -0800160 return filtered.iterator().next();
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800161 }
162
163 /**
helenyrwu2a674902016-07-20 09:48:04 -0700164 * Computes a disjoint path between two ConnectPoints.
165 *
166 * @param intent intent on which behalf path is being computed
167 * @param one start of the path
168 * @param two end of the path
169 * @return DisjointPath between the two
170 * @throws PathNotFoundException if two paths cannot be found
171 */
172 protected DisjointPath getDisjointPath(ConnectivityIntent intent,
173 ElementId one, ElementId two) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300174 Set<DisjointPath> paths = pathService.getDisjointPaths(one, two, weigher(intent.constraints()));
helenyrwu2a674902016-07-20 09:48:04 -0700175 final List<Constraint> constraints = intent.constraints();
176 ImmutableList<DisjointPath> filtered = FluentIterable.from(paths)
177 .filter(path -> checkPath(path, constraints))
Yuta HIGUCHI8c47efe2017-03-29 19:25:24 -0700178 .filter(path -> checkPath(path.backup(), constraints))
helenyrwu2a674902016-07-20 09:48:04 -0700179 .toList();
180 if (filtered.isEmpty()) {
181 throw new PathNotFoundException(one, two);
182 }
jaegonkim7e876632017-01-25 06:01:49 +0900183
184 if (constraints.stream().anyMatch(c -> c instanceof HashedPathSelectionConstraint)) {
185 return filtered.get(intent.hashCode() % filtered.size());
186 }
187
helenyrwu2a674902016-07-20 09:48:04 -0700188 return filtered.iterator().next();
189 }
190
191 /**
Luca Pretede10c782017-01-05 17:23:08 -0800192 * Allocates the bandwidth specified as intent constraint on each link
193 * composing the intent, if a bandwidth constraint is specified.
194 *
195 * @param intent the intent requesting bandwidth allocation
196 * @param connectPoints the connect points composing the intent path computed
197 */
198 protected void allocateBandwidth(ConnectivityIntent intent,
199 List<ConnectPoint> connectPoints) {
200 // Retrieve bandwidth constraint if exists
201 List<Constraint> constraints = intent.constraints();
202
203 if (constraints == null) {
204 return;
205 }
206
207 Optional<Constraint> constraint =
208 constraints.stream()
209 .filter(c -> c instanceof BandwidthConstraint)
210 .findAny();
211
212 // If there is no bandwidth constraint continue
213 if (!constraint.isPresent()) {
214 return;
215 }
216
217 BandwidthConstraint bwConstraint = (BandwidthConstraint) constraint.get();
218
219 double bw = bwConstraint.bandwidth().bps();
220
221 // If a resource group is set on the intent, the resource consumer is
222 // set equal to it. Otherwise it's set to the intent key
223 ResourceConsumer newResourceConsumer =
224 intent.resourceGroup() != null ? intent.resourceGroup() : intent.key();
225
226 // Get the list of current resource allocations
227 Collection<ResourceAllocation> resourceAllocations =
228 resourceService.getResourceAllocations(newResourceConsumer);
229
230 // Get the list of resources already allocated from resource allocations
231 List<Resource> resourcesAllocated =
232 resourcesFromAllocations(resourceAllocations);
233
234 // Get the list of resource ids for resources already allocated
235 List<ResourceId> idsResourcesAllocated = resourceIds(resourcesAllocated);
236
237 // Create the list of incoming resources requested. Exclude resources
238 // already allocated.
239 List<Resource> incomingResources =
240 resources(connectPoints, bw).stream()
241 .filter(r -> !resourcesAllocated.contains(r))
242 .collect(Collectors.toList());
243
244 if (incomingResources.isEmpty()) {
245 return;
246 }
247
248 // Create the list of resources to be added, meaning their key is not
249 // present in the resources already allocated
250 List<Resource> resourcesToAdd =
251 incomingResources.stream()
252 .filter(r -> !idsResourcesAllocated.contains(r.id()))
253 .collect(Collectors.toList());
254
255 // Resources to updated are all the new valid resources except the
256 // resources to be added
257 List<Resource> resourcesToUpdate = Lists.newArrayList(incomingResources);
258 resourcesToUpdate.removeAll(resourcesToAdd);
259
260 // If there are no resources to update skip update procedures
261 if (!resourcesToUpdate.isEmpty()) {
262 // Remove old resources that need to be updated
263 // TODO: use transaction updates when available in the resource service
264 List<ResourceAllocation> resourceAllocationsToUpdate =
265 resourceAllocations.stream()
266 .filter(rA -> resourceIds(resourcesToUpdate).contains(rA.resource().id()))
267 .collect(Collectors.toList());
268 log.debug("Releasing bandwidth for intent {}: {} bps", newResourceConsumer, resourcesToUpdate);
269 resourceService.release(resourceAllocationsToUpdate);
270
271 // Update resourcesToAdd with the list of both the new resources and
272 // the resources to update
273 resourcesToAdd.addAll(resourcesToUpdate);
274 }
275
276 // Look also for resources allocated using the intent key and -if any-
277 // remove them
278 if (intent.resourceGroup() != null) {
279 // Get the list of current resource allocations made by intent key
280 Collection<ResourceAllocation> resourceAllocationsByKey =
281 resourceService.getResourceAllocations(intent.key());
282
283 resourceService.release(Lists.newArrayList(resourceAllocationsByKey));
284 }
285
286 // Allocate resources
287 log.debug("Allocating bandwidth for intent {}: {} bps", newResourceConsumer, resourcesToAdd);
288 List<ResourceAllocation> allocations =
289 resourceService.allocate(newResourceConsumer, resourcesToAdd);
290
291 if (allocations.isEmpty()) {
292 log.debug("No resources allocated for intent {}", newResourceConsumer);
293 }
294
295 log.debug("Done allocating bandwidth for intent {}", newResourceConsumer);
296 }
297
298 /**
299 * Produces a list of resources from a list of resource allocations.
300 *
301 * @param rAs the list of resource allocations
302 * @return a list of resources retrieved from the resource allocations given
303 */
304 private static List<Resource> resourcesFromAllocations(Collection<ResourceAllocation> rAs) {
305 return rAs.stream()
306 .map(ResourceAllocation::resource)
307 .collect(Collectors.toList());
308 }
309
310 /**
311 * Creates a list of continuous bandwidth resources given a list of connect
312 * points and a bandwidth.
313 *
314 * @param cps the list of connect points
315 * @param bw the bandwidth expressed as a double
316 * @return the list of resources
317 */
318 private static List<Resource> resources(List<ConnectPoint> cps, double bw) {
319 return cps.stream()
320 // Make sure the element id is a valid device id
321 .filter(cp -> cp.elementId() instanceof DeviceId)
322 // Create a continuous resource for each CP we're going through
323 .map(cp -> Resources.continuous(cp.deviceId(), cp.port(),
324 Bandwidth.class).resource(bw))
325 .collect(Collectors.toList());
326 }
327
328 /**
329 * Returns a list of resource ids given a list of resources.
330 *
331 * @param resources the list of resources
332 * @return the list of resource ids retrieved from the resources given
333 */
334 private static List<ResourceId> resourceIds(List<Resource> resources) {
335 return resources.stream()
336 .map(Resource::id)
337 .collect(Collectors.toList());
338 }
339
340 /**
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800341 * Edge-weight capable of evaluating link cost using a set of constraints.
342 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300343 protected class ConstraintBasedLinkWeigher extends DefaultEdgeWeigher<TopologyVertex, TopologyEdge>
344 implements LinkWeigher {
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800345
346 private final List<Constraint> constraints;
347
348 /**
349 * Creates a new edge-weight function capable of evaluating links
350 * on the basis of the specified constraints.
351 *
352 * @param constraints path constraints
353 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300354 ConstraintBasedLinkWeigher(List<Constraint> constraints) {
Sho SHIMIZU56531512014-11-10 15:27:49 -0800355 if (constraints == null) {
356 this.constraints = Collections.emptyList();
357 } else {
358 this.constraints = ImmutableList.copyOf(constraints);
359 }
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800360 }
361
362 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +0300363 public Weight weight(TopologyEdge edge) {
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800364
365 // iterate over all constraints in order and return the weight of
366 // the first one with fast fail over the first failure
Yuta HIGUCHIe37560f2017-02-02 19:53:26 -0800367 Iterator<Constraint> it = constraints.stream()
368 .filter(c -> !(c instanceof MarkerConstraint))
369 .filter(c -> !(c instanceof PathViabilityConstraint))
370 .iterator();
Ray Milkey460f4022014-11-05 15:41:43 -0800371
Yuta HIGUCHIa0b0a132016-09-16 15:47:49 -0700372 if (!it.hasNext()) {
Yuta HIGUCHIfa9ee8c2017-02-02 20:00:14 -0800373 return DEFAULT_HOP_WEIGHT;
Yuta HIGUCHIa0b0a132016-09-16 15:47:49 -0700374 }
375
Sho SHIMIZUb1681bd2016-02-22 12:47:50 -0800376 double cost = it.next().cost(edge.link(), resourceService::isAvailable);
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800377 while (it.hasNext() && cost > 0) {
Sho SHIMIZUb1681bd2016-02-22 12:47:50 -0800378 if (it.next().cost(edge.link(), resourceService::isAvailable) < 0) {
Yuta HIGUCHIe37560f2017-02-02 19:53:26 -0800379 // TODO shouldn't this be non-viable?
Andrey Komarov2398d962016-09-26 15:11:23 +0300380 cost = -1;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800381 }
382 }
Yuta HIGUCHIe37560f2017-02-02 19:53:26 -0800383 return ScalarWeight.toWeight(cost);
Ray Milkey460f4022014-11-05 15:41:43 -0800384
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800385 }
386 }
387
388}