blob: 82729be3b596a0b8842cbecb1657090595a7fef9 [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.Component;
22import org.osgi.service.component.annotations.Reference;
23import org.osgi.service.component.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
Ray Milkeyd84f89b2018-08-17 14:54:17 -070077 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Luca Pretede10c782017-01-05 17:23:08 -080078 protected DeviceService deviceService;
79
Ray Milkeyd84f89b2018-08-17 14:54:17 -070080 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080081 protected IntentExtensionService intentManager;
82
Ray Milkeyd84f89b2018-08-17 14:54:17 -070083 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080084 protected PathService pathService;
85
Ray Milkeyd84f89b2018-08-17 14:54:17 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY)
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 /**
helenyrwu2a674902016-07-20 09:48:04 -0700166 * Computes a disjoint path 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 DisjointPath between the two
172 * @throws PathNotFoundException if two paths cannot be found
173 */
174 protected DisjointPath getDisjointPath(ConnectivityIntent intent,
175 ElementId one, ElementId two) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300176 Set<DisjointPath> paths = pathService.getDisjointPaths(one, two, weigher(intent.constraints()));
helenyrwu2a674902016-07-20 09:48:04 -0700177 final List<Constraint> constraints = intent.constraints();
178 ImmutableList<DisjointPath> filtered = FluentIterable.from(paths)
179 .filter(path -> checkPath(path, constraints))
Yuta HIGUCHI8c47efe2017-03-29 19:25:24 -0700180 .filter(path -> checkPath(path.backup(), constraints))
helenyrwu2a674902016-07-20 09:48:04 -0700181 .toList();
182 if (filtered.isEmpty()) {
183 throw new PathNotFoundException(one, two);
184 }
jaegonkim7e876632017-01-25 06:01:49 +0900185
186 if (constraints.stream().anyMatch(c -> c instanceof HashedPathSelectionConstraint)) {
187 return filtered.get(intent.hashCode() % filtered.size());
188 }
189
helenyrwu2a674902016-07-20 09:48:04 -0700190 return filtered.iterator().next();
191 }
192
193 /**
Luca Pretede10c782017-01-05 17:23:08 -0800194 * Allocates the bandwidth specified as intent constraint on each link
195 * composing the intent, if a bandwidth constraint is specified.
196 *
197 * @param intent the intent requesting bandwidth allocation
198 * @param connectPoints the connect points composing the intent path computed
199 */
200 protected void allocateBandwidth(ConnectivityIntent intent,
201 List<ConnectPoint> connectPoints) {
202 // Retrieve bandwidth constraint if exists
203 List<Constraint> constraints = intent.constraints();
204
205 if (constraints == null) {
206 return;
207 }
208
209 Optional<Constraint> constraint =
210 constraints.stream()
211 .filter(c -> c instanceof BandwidthConstraint)
212 .findAny();
213
214 // If there is no bandwidth constraint continue
215 if (!constraint.isPresent()) {
216 return;
217 }
218
219 BandwidthConstraint bwConstraint = (BandwidthConstraint) constraint.get();
220
221 double bw = bwConstraint.bandwidth().bps();
222
223 // If a resource group is set on the intent, the resource consumer is
224 // set equal to it. Otherwise it's set to the intent key
225 ResourceConsumer newResourceConsumer =
226 intent.resourceGroup() != null ? intent.resourceGroup() : intent.key();
227
228 // Get the list of current resource allocations
229 Collection<ResourceAllocation> resourceAllocations =
230 resourceService.getResourceAllocations(newResourceConsumer);
231
232 // Get the list of resources already allocated from resource allocations
233 List<Resource> resourcesAllocated =
234 resourcesFromAllocations(resourceAllocations);
235
236 // Get the list of resource ids for resources already allocated
237 List<ResourceId> idsResourcesAllocated = resourceIds(resourcesAllocated);
238
239 // Create the list of incoming resources requested. Exclude resources
240 // already allocated.
241 List<Resource> incomingResources =
242 resources(connectPoints, bw).stream()
243 .filter(r -> !resourcesAllocated.contains(r))
244 .collect(Collectors.toList());
245
246 if (incomingResources.isEmpty()) {
247 return;
248 }
249
250 // Create the list of resources to be added, meaning their key is not
251 // present in the resources already allocated
252 List<Resource> resourcesToAdd =
253 incomingResources.stream()
254 .filter(r -> !idsResourcesAllocated.contains(r.id()))
255 .collect(Collectors.toList());
256
257 // Resources to updated are all the new valid resources except the
258 // resources to be added
259 List<Resource> resourcesToUpdate = Lists.newArrayList(incomingResources);
260 resourcesToUpdate.removeAll(resourcesToAdd);
261
262 // If there are no resources to update skip update procedures
263 if (!resourcesToUpdate.isEmpty()) {
264 // Remove old resources that need to be updated
265 // TODO: use transaction updates when available in the resource service
266 List<ResourceAllocation> resourceAllocationsToUpdate =
267 resourceAllocations.stream()
268 .filter(rA -> resourceIds(resourcesToUpdate).contains(rA.resource().id()))
269 .collect(Collectors.toList());
270 log.debug("Releasing bandwidth for intent {}: {} bps", newResourceConsumer, resourcesToUpdate);
271 resourceService.release(resourceAllocationsToUpdate);
272
273 // Update resourcesToAdd with the list of both the new resources and
274 // the resources to update
275 resourcesToAdd.addAll(resourcesToUpdate);
276 }
277
278 // Look also for resources allocated using the intent key and -if any-
279 // remove them
280 if (intent.resourceGroup() != null) {
281 // Get the list of current resource allocations made by intent key
282 Collection<ResourceAllocation> resourceAllocationsByKey =
283 resourceService.getResourceAllocations(intent.key());
284
285 resourceService.release(Lists.newArrayList(resourceAllocationsByKey));
286 }
287
288 // Allocate resources
289 log.debug("Allocating bandwidth for intent {}: {} bps", newResourceConsumer, resourcesToAdd);
290 List<ResourceAllocation> allocations =
291 resourceService.allocate(newResourceConsumer, resourcesToAdd);
292
293 if (allocations.isEmpty()) {
294 log.debug("No resources allocated for intent {}", newResourceConsumer);
295 }
296
297 log.debug("Done allocating bandwidth for intent {}", newResourceConsumer);
298 }
299
300 /**
301 * Produces a list of resources from a list of resource allocations.
302 *
303 * @param rAs the list of resource allocations
304 * @return a list of resources retrieved from the resource allocations given
305 */
306 private static List<Resource> resourcesFromAllocations(Collection<ResourceAllocation> rAs) {
307 return rAs.stream()
308 .map(ResourceAllocation::resource)
309 .collect(Collectors.toList());
310 }
311
312 /**
313 * Creates a list of continuous bandwidth resources given a list of connect
314 * points and a bandwidth.
315 *
316 * @param cps the list of connect points
317 * @param bw the bandwidth expressed as a double
318 * @return the list of resources
319 */
320 private static List<Resource> resources(List<ConnectPoint> cps, double bw) {
321 return cps.stream()
322 // Make sure the element id is a valid device id
323 .filter(cp -> cp.elementId() instanceof DeviceId)
324 // Create a continuous resource for each CP we're going through
325 .map(cp -> Resources.continuous(cp.deviceId(), cp.port(),
326 Bandwidth.class).resource(bw))
327 .collect(Collectors.toList());
328 }
329
330 /**
331 * Returns a list of resource ids given a list of resources.
332 *
333 * @param resources the list of resources
334 * @return the list of resource ids retrieved from the resources given
335 */
336 private static List<ResourceId> resourceIds(List<Resource> resources) {
337 return resources.stream()
338 .map(Resource::id)
339 .collect(Collectors.toList());
340 }
341
342 /**
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800343 * Edge-weight capable of evaluating link cost using a set of constraints.
344 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300345 protected class ConstraintBasedLinkWeigher extends DefaultEdgeWeigher<TopologyVertex, TopologyEdge>
346 implements LinkWeigher {
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800347
348 private final List<Constraint> constraints;
349
350 /**
351 * Creates a new edge-weight function capable of evaluating links
352 * on the basis of the specified constraints.
353 *
354 * @param constraints path constraints
355 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300356 ConstraintBasedLinkWeigher(List<Constraint> constraints) {
Sho SHIMIZU56531512014-11-10 15:27:49 -0800357 if (constraints == null) {
358 this.constraints = Collections.emptyList();
359 } else {
360 this.constraints = ImmutableList.copyOf(constraints);
361 }
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800362 }
363
364 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +0300365 public Weight weight(TopologyEdge edge) {
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800366
367 // iterate over all constraints in order and return the weight of
368 // the first one with fast fail over the first failure
Yuta HIGUCHIe37560f2017-02-02 19:53:26 -0800369 Iterator<Constraint> it = constraints.stream()
370 .filter(c -> !(c instanceof MarkerConstraint))
371 .filter(c -> !(c instanceof PathViabilityConstraint))
372 .iterator();
Ray Milkey460f4022014-11-05 15:41:43 -0800373
Yuta HIGUCHIa0b0a132016-09-16 15:47:49 -0700374 if (!it.hasNext()) {
Yuta HIGUCHIfa9ee8c2017-02-02 20:00:14 -0800375 return DEFAULT_HOP_WEIGHT;
Yuta HIGUCHIa0b0a132016-09-16 15:47:49 -0700376 }
377
Sho SHIMIZUb1681bd2016-02-22 12:47:50 -0800378 double cost = it.next().cost(edge.link(), resourceService::isAvailable);
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800379 while (it.hasNext() && cost > 0) {
Sho SHIMIZUb1681bd2016-02-22 12:47:50 -0800380 if (it.next().cost(edge.link(), resourceService::isAvailable) < 0) {
Yuta HIGUCHIe37560f2017-02-02 19:53:26 -0800381 // TODO shouldn't this be non-viable?
Andrey Komarov2398d962016-09-26 15:11:23 +0300382 cost = -1;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800383 }
384 }
Yuta HIGUCHIe37560f2017-02-02 19:53:26 -0800385 return ScalarWeight.toWeight(cost);
Ray Milkey460f4022014-11-05 15:41:43 -0800386
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800387 }
388 }
389
390}