blob: 451394524e2d30ea53cd83f99ac9136f151920fd [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.intent.impl.installer;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.tuple.Pair;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.onosproject.net.DeviceId;
import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription;
import org.onosproject.net.behaviour.protection.ProtectionConfig;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentException;
import org.onosproject.net.intent.IntentExtensionService;
import org.onosproject.net.intent.IntentInstallCoordinator;
import org.onosproject.net.intent.IntentOperationContext;
import org.onosproject.net.intent.IntentInstaller;
import org.onosproject.net.intent.ProtectionEndpointIntent;
import org.onosproject.net.intent.impl.IntentManager;
import org.onosproject.net.intent.ObjectiveTrackerService;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.net.config.NetworkConfigEvent.Type.*;
import static org.onosproject.net.intent.IntentInstaller.Direction.ADD;
import static org.onosproject.net.intent.IntentInstaller.Direction.REMOVE;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Installer for ProtectionEndpointIntent.
*/
@Component(immediate = true)
public class ProtectionEndpointIntentInstaller implements IntentInstaller<ProtectionEndpointIntent> {
private static final String CONFIG_FAILED = "Config operation unsuccessful, expected %s, actual %s.";
private final Logger log = getLogger(IntentManager.class);
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected IntentExtensionService intentExtensionService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
NetworkConfigService networkConfigService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
IntentInstallCoordinator intentInstallCoordinator;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
ObjectiveTrackerService trackerService;
@Activate
public void activate() {
intentExtensionService.registerInstaller(ProtectionEndpointIntent.class, this);
}
@Deactivate
public void deactivated() {
intentExtensionService.unregisterInstaller(ProtectionEndpointIntent.class);
}
@Override
public void apply(IntentOperationContext<ProtectionEndpointIntent> context) {
Optional<IntentData> toUninstall = context.toUninstall();
Optional<IntentData> toInstall = context.toInstall();
List<ProtectionEndpointIntent> uninstallIntents = context.intentsToUninstall();
List<ProtectionEndpointIntent> installIntents = context.intentsToInstall();
if (!toInstall.isPresent() && !toUninstall.isPresent()) {
intentInstallCoordinator.intentInstallSuccess(context);
return;
}
if (toUninstall.isPresent()) {
IntentData intentData = toUninstall.get();
trackerService.removeTrackedResources(intentData.key(), intentData.intent().resources());
uninstallIntents.forEach(installable ->
trackerService.removeTrackedResources(intentData.intent().key(),
installable.resources()));
}
if (toInstall.isPresent()) {
IntentData intentData = toInstall.get();
trackerService.addTrackedResources(intentData.key(), intentData.intent().resources());
installIntents.forEach(installable ->
trackerService.addTrackedResources(intentData.key(),
installable.resources()));
}
List<Stage> stages = new ArrayList<>();
stages.add(new Stage(uninstallIntents.stream()
.map(i -> Pair.of(i, REMOVE))
.collect(Collectors.toList())));
stages.add(new Stage(installIntents.stream()
.map(i -> Pair.of(i, ADD))
.collect(Collectors.toList())));
for (Stage stage : stages) {
log.debug("applying Stage {}", stage);
try {
// wait for stage completion
stage.apply();
stage.listeners().forEach(networkConfigService::removeListener);
} catch (IntentException e) {
log.error("Stage {} failed, reason: {}", stage, e.toString());
intentInstallCoordinator.intentInstallFailed(context);
return;
}
}
// All stage success
intentInstallCoordinator.intentInstallSuccess(context);
}
/**
* Stage of installable Intents which can be processed in parallel.
*/
private final class Stage {
// should it have progress state, how far it went?
private final Collection<Pair<ProtectionEndpointIntent, Direction>> ops;
private final Set<NetworkConfigListener> listeners = Sets.newHashSet();
/**
* Create a stage with given operations.
*
* @param ops the operations
*/
Stage(Collection<Pair<ProtectionEndpointIntent, Direction>> ops) {
this.ops = checkNotNull(ops);
}
/**
* Applies all operations for this stage.
*
* @return the CompletableFuture object for this operation
*/
void apply() {
ops.stream()
.map(op -> applyOp(op.getRight(), op.getLeft()))
.forEach(future -> {
try {
future.get(100, TimeUnit.MILLISECONDS);
} catch (TimeoutException | InterruptedException | ExecutionException e) {
throw new IntentException(e.toString());
}
});
}
/**
* Applies the protection endpoint Intent with a given direction.
*
* @param dir the direction
* @param intent the protection endpoint Intent
* @return the CompletableFuture object for this operation
*/
private CompletableFuture<Void> applyOp(Direction dir, ProtectionEndpointIntent intent) {
log.trace("applying {}: {}", dir, intent);
if (dir == REMOVE) {
ProtectionConfigListener listener =
new ProtectionConfigListener(ImmutableSet.of(CONFIG_REMOVED),
intent.deviceId());
networkConfigService.addListener(listener);
listeners.add(listener);
networkConfigService.removeConfig(intent.deviceId(), ProtectionConfig.class);
return listener.completableFuture();
} else {
ProtectedTransportEndpointDescription description = intent.description();
// Can't do following. Will trigger empty CONFIG_ADDED
//ProtectionConfig cfg = networkConfigService.addConfig(intent.deviceId(),
// ProtectionConfig.class);
ProtectionConfig cfg = new ProtectionConfig(intent.deviceId());
cfg.fingerprint(description.fingerprint());
cfg.peer(description.peer());
cfg.paths(description.paths());
ProtectionConfigListener listener =
new ProtectionConfigListener(ImmutableSet.of(CONFIG_ADDED, CONFIG_UPDATED),
intent.deviceId());
networkConfigService.addListener(listener);
listeners.add(listener);
networkConfigService.applyConfig(intent.deviceId(),
ProtectionConfig.class,
cfg.node());
return listener.completableFuture();
}
}
@Override
public String toString() {
return ops.toString();
}
public Set<NetworkConfigListener> listeners() {
return listeners;
}
/**
* Listener for protection config for specific config event and device.
*/
class ProtectionConfigListener implements NetworkConfigListener {
private CompletableFuture<Void> completableFuture;
private Set<NetworkConfigEvent.Type> listenTypes;
private DeviceId listenDevice;
public ProtectionConfigListener(Set<NetworkConfigEvent.Type> listenTypes, DeviceId listenDevice) {
completableFuture = new CompletableFuture<>();
this.listenTypes = listenTypes;
this.listenDevice = listenDevice;
}
@Override
public void event(NetworkConfigEvent event) {
if (!event.subject().equals(listenDevice)) {
return;
}
if (!listenTypes.contains(event.type())) {
String errorMsg = String.format(CONFIG_FAILED, listenTypes.toString(), event.type());
completableFuture.completeExceptionally(new IntentException(errorMsg));
} else {
completableFuture.complete(null);
}
}
public CompletableFuture<Void> completableFuture() {
return completableFuture;
}
}
}
}