IntentMonitorAndReroute initial contribution
Change-Id: I88616235b1e8ae28894da75b3fc8d46cb209dac5
diff --git a/apps/imr/app/BUCK b/apps/imr/app/BUCK
new file mode 100644
index 0000000..4f39ad8
--- /dev/null
+++ b/apps/imr/app/BUCK
@@ -0,0 +1,17 @@
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+ '//core/store/dist:onos-core-dist',
+ '//core/store/serializers:onos-core-serializers',
+ '//incubator/api:onos-incubator-api',
+ '//lib:KRYO',
+ '//lib:JACKSON',
+]
+
+TEST_DEPS = [
+ '//lib:TEST_ADAPTERS',
+]
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+ test_deps = TEST_DEPS,
+)
diff --git a/apps/imr/app/pom.xml b/apps/imr/app/pom.xml
new file mode 100644
index 0000000..b66ebc6
--- /dev/null
+++ b/apps/imr/app/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-apps</artifactId>
+ <version>1.13.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-app-imr-app</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>Intent Monitoring and Rerouting application</description>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-misc</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-incubator-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-imr-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-core-net</artifactId>
+ <version>1.13.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/IntentMonitorAndRerouteManager.java b/apps/imr/app/src/main/java/org/onosproject/imr/IntentMonitorAndRerouteManager.java
new file mode 100644
index 0000000..3ab49aa
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/IntentMonitorAndRerouteManager.java
@@ -0,0 +1,642 @@
+/*
+ * 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.imr;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.imr.data.Path;
+import org.onosproject.imr.data.Route;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleListener;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.ConnectivityIntent;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.statistic.FlowStatisticStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.DistributedSet;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Manager of Intent Monitor and Reroute.
+ */
+@Component(immediate = true)
+@Service
+public class IntentMonitorAndRerouteManager implements IntentMonitorAndRerouteService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private ConsistentMap<ApplicationId, Map<Key, ConnectivityIntent>> monitoredIntentsDistr;
+ private Map<ApplicationId, Map<Key, ConnectivityIntent>> monitoredIntents;
+
+ private DistributedSet<Key> toBeMonitoredIntents;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService intentService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowStatisticStore statsStore;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ private InternalIntentListener intentListener = new InternalIntentListener();
+
+ private InternalFlowRuleListener flowRuleListener = new InternalFlowRuleListener();
+
+ @Activate
+ protected void activate() {
+ intentService.addListener(intentListener);
+ flowRuleService.addListener(flowRuleListener);
+
+ monitoredIntentsDistr = storageService
+ .<ApplicationId, Map<Key, ConnectivityIntent>>consistentMapBuilder()
+ .withSerializer(Serializer.using(KryoNamespaces.API))
+ .withName("IMR-monitoredIntents")
+ .build();
+ monitoredIntents = monitoredIntentsDistr.asJavaMap();
+
+ toBeMonitoredIntents = storageService.<Key>setBuilder()
+ .withSerializer(Serializer.using(
+ new KryoNamespace.Builder()
+ .register(KryoNamespaces.API)
+ .register(Key.class)
+ .build()))
+ .withName("IMR-toMonitorIntents")
+ .build()
+ .asDistributedSet();
+ log.info("IntentMonitorAndReroute activated");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ intentService.removeListener(intentListener);
+ flowRuleService.removeListener(flowRuleListener);
+ monitoredIntents
+ .forEach(((applicationId, keyConnectivityIntentMap) ->
+ keyConnectivityIntentMap.keySet()
+ .forEach(this::removeIntent)));
+ log.info("IntentMonitorAndReroute deactivated");
+ }
+
+
+ private synchronized void storeMonitoredIntent(ConnectivityIntent intent) {
+ log.debug("Store Monitored Intent {}", intent.key());
+ Map<Key, ConnectivityIntent> temp = monitoredIntents.getOrDefault(intent.appId(), new ConcurrentHashMap<>());
+ temp.put(intent.key(), intent);
+ monitoredIntents.put(intent.appId(), temp);
+ }
+
+ @Override
+ public synchronized boolean startMonitorIntent(Key intentKey) {
+ checkNotNull(intentKey, "Intent Key must not be null");
+ log.debug("Start Monitor Intent: {}", intentKey.toString());
+ toBeMonitoredIntents.add(intentKey);
+
+ //Check if the requested intent is already present in the intent manager
+ Intent installedIntent = intentService.getIntent(intentKey);
+ if (!allowedIntent(installedIntent)) {
+ return false;
+ }
+ //Check if the intent that is present in the intent subsystem is already installed
+ if (intentService.getIntentState(intentKey) == IntentState.INSTALLED) {
+ storeMonitoredIntent((ConnectivityIntent) installedIntent);
+ }
+ return true;
+ }
+
+
+ /**
+ * Returns whether the intent can be monitored or not.
+ * @param intent The intent you want to check if it is allowed to be monitored.
+ * @return true if the intent's type is of one of the allowed types
+ * ({@link LinkCollectionIntent}, {@link PointToPointIntent}).
+ */
+ public boolean allowedIntent(Intent intent) {
+ return intent instanceof LinkCollectionIntent || intent instanceof PointToPointIntent;
+ }
+
+ @Override
+ public synchronized boolean stopMonitorIntent(Key intentKey) {
+ checkNotNull(intentKey, "Intent key must not be null");
+ log.debug("Stop Monitor Intent: ", intentKey.toString());
+ if (!toBeMonitoredIntents.contains(intentKey)) {
+ return false;
+ }
+ removeIntent(intentKey);
+ toBeMonitoredIntents.remove(intentKey);
+ return true;
+ }
+
+ /**
+ * Removes the intent from the internal structure.
+ * @param intentKey Key of the intent to be removed.
+ * @return true if the intent is found and removed, false otherwise.
+ */
+ private synchronized boolean removeIntent(Key intentKey) {
+ for (Map.Entry<ApplicationId, Map<Key, ConnectivityIntent>> appIntents
+ : monitoredIntents.entrySet()) {
+ if (appIntents.getValue().containsKey(intentKey)) {
+ appIntents.getValue().remove(intentKey);
+ //TODO: check if it works without reputting the map
+ flushIntentStatStore(intentKey);
+ monitoredIntents.put(appIntents.getKey(), appIntents.getValue());
+ if (appIntents.getValue().isEmpty()) {
+ monitoredIntents.remove(appIntents.getKey());
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Flushes the statistics (from the statistics store) of an intent.
+ * @param intentKey Key of the intent which statistics has to be cleaned.
+ */
+ private synchronized void flushIntentStatStore(Key intentKey) {
+ checkNotNull(intentKey);
+ //Remove all the flow rule on the stats store related to the passed intentKey
+ intentService.getInstallableIntents(intentKey)
+ .stream()
+ .map(intent -> (FlowRuleIntent) intent)
+ .forEach(intent -> intent.flowRules()
+ .forEach(flowRule -> statsStore.removeFlowStatistic(flowRule))
+ );
+ }
+
+
+ /**
+ * Generates a new {@Link LinkCollectionIntent} applying the new path.
+ * @param links List of links of the new path.
+ * @param intentKey Key of the intent you want to re-route.
+ * @param appId Application id that submits initially the intent.
+ * @return The new intent, if not possibile it will return the old intent already installed.
+ */
+ private ConnectivityIntent generateLinkCollectionIntent(
+ List<Link> links,
+ Key intentKey,
+ ApplicationId appId) {
+ checkNotNull(links);
+ checkNotNull(appId);
+
+ // Gets the oldIntent already installed
+ ConnectivityIntent oldIntent = monitoredIntents.get(appId).get(intentKey);
+
+ //Flush the statistics of the currently installed intent
+ flushIntentStatStore(intentKey);
+
+ //get the connect point of the old intent
+ // Left element of the Pair is the ingress, right one is the egress
+ Pair<Set<FilteredConnectPoint>, Set<FilteredConnectPoint>> cpPair = extractEndConnectPoints(oldIntent);
+ if (cpPair == null) {
+ return oldIntent;
+ }
+
+ // Now generate the new intent
+ LinkCollectionIntent newIntent = LinkCollectionIntent.builder()
+ .appId(oldIntent.appId())
+ .key(intentKey)
+ .selector(oldIntent.selector())
+ .filteredIngressPoints(ImmutableSet.copyOf(cpPair.getLeft()))
+ .filteredEgressPoints(ImmutableSet.copyOf(cpPair.getRight()))
+ .treatment(oldIntent.treatment())
+ .priority(oldIntent.priority())
+ .constraints(oldIntent.constraints())
+ .links(ImmutableSet.copyOf(links))
+ //TODO: is there a way to get from the old intent?
+ .applyTreatmentOnEgress(true)
+ .build();
+
+ return newIntent;
+ }
+
+ @Override
+ public boolean applyPath(Route route) {
+ checkNotNull(route, "Route to apply must be not null");
+ checkNotNull(route.appId(), "Application id must be not null");
+ checkNotNull(route.key(), "Intent key to apply must be not null");
+ checkNotNull(route.paths(), "New path must be not null");
+ checkArgument(route.paths().size() >= 1);
+
+ ApplicationId appId = route.appId();
+ Key key = route.key();
+
+ // check if the app and the intent key are monitored
+ if (!monitoredIntents.containsKey(appId)) {
+ return false;
+ }
+ if (!monitoredIntents.get(appId).containsKey(key)) {
+ return false;
+ }
+
+ // TODO: now we manage only the unsplittable routing
+ Path currentPath = route.paths()
+ .stream()
+ .max(Path::compareTo)
+ .get();
+
+ // Check if the last and first element of the path are HostId
+ // in this case remove them from the list
+ if (currentPath.path().get(0) instanceof HostId) {
+ currentPath.path().remove(0);
+ }
+ if (currentPath.path().get(currentPath.path().size() - 1) instanceof HostId) {
+ currentPath.path().remove(currentPath.path().size() - 1);
+ }
+
+ List<Link> links = createPathFromDeviceList(currentPath.path());
+
+ // Generate the new Link collection intent, if not possible it will return the old intent
+ ConnectivityIntent intent = generateLinkCollectionIntent(links, key, appId);
+ storeMonitoredIntent(intent);
+ intentService.submit(intent);
+ return true;
+ }
+
+ @Override
+ public Map<ApplicationId, Map<Key, List<FlowEntry>>> getStats() {
+ //TODO: check if there is a better way to get the statistics
+ Map<ApplicationId, Map<Key, List<FlowEntry>>> currentStatistics = new HashMap<>();
+ monitoredIntents.forEach((appId, mapIntentKey) ->
+ currentStatistics.putAll(getStats(appId))
+ );
+ return currentStatistics;
+ }
+
+ @Override
+ public Map<ApplicationId, Map<Key, List<FlowEntry>>> getStats(ApplicationId appId) {
+ checkNotNull(appId);
+
+ //TODO: is there a better way to get statistics?
+ Map<ApplicationId, Map<Key, List<FlowEntry>>> currentStatistics = new HashMap<>();
+ currentStatistics.put(appId, new HashMap<>());
+ if (monitoredIntents.containsKey(appId)) {
+ Set<Key> keySet = monitoredIntents.get(appId).keySet();
+ for (Key intentKey : keySet) {
+
+ List<FlowEntry> flowEntries = getStats(intentKey);
+ currentStatistics.get(appId).put(intentKey, flowEntries);
+ }
+ }
+ return currentStatistics;
+ }
+
+ @Override
+ public Map<ApplicationId, Map<Key, List<FlowEntry>>> getStats(ApplicationId appId, Key intentKey) {
+ checkNotNull(appId);
+ checkNotNull(intentKey);
+ checkArgument(monitoredIntents.containsKey(appId));
+ checkArgument(monitoredIntents.get(appId).containsKey(intentKey));
+
+ Map<ApplicationId, Map<Key, List<FlowEntry>>> currentStatistics = new HashMap<>();
+ currentStatistics.put(appId, new HashMap<>());
+ List<FlowEntry> flowEntries = getStats(intentKey);
+ currentStatistics.get(appId).put(intentKey, flowEntries);
+ return currentStatistics;
+ }
+
+ /**
+ * Returns the list of flow entries of a particular intent.
+ * @param intentKey
+ * @return List of the flow entries of the specified intent,
+ * it contains all the statistics of that intent.
+ */
+ private List<FlowEntry> getStats(Key intentKey) {
+ List<FlowEntry> currentStatistics = new LinkedList<>();
+ intentService.getInstallableIntents(intentKey)
+ .forEach(intent -> ((FlowRuleIntent) intent).flowRules()
+ .forEach(flowRule -> {
+ ConnectPoint cp = buildConnectPoint(flowRule);
+ currentStatistics.addAll(getStats(cp, flowRule));
+ })
+ );
+ return currentStatistics;
+ }
+
+ /**
+ * Returns a list of flow entry related to the connect point and flow rule passed.
+ * @param cp ConnectPoint we want to retrieve the flow entry from.
+ * @param flowRule FlowRule.
+ * @return List of flow entries.
+ */
+ private List<FlowEntry> getStats(ConnectPoint cp, FlowRule flowRule) {
+ return statsStore.getCurrentFlowStatistic(cp)
+ .stream()
+ .filter(flowEntry -> flowEntry
+ .id()
+ .equals(flowRule.id()))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns a list of links starting from a list of devices.
+ * @param deviceList List of devices.
+ * @return A path in terms of list of links.
+ */
+ private List<Link> createPathFromDeviceList(List<ElementId> deviceList) {
+ List<Link> path = new ArrayList<>();
+ if (deviceList.size() == 1) {
+ return path;
+ }
+
+ // Left element represents the input and right the output
+ List<Pair<DeviceId, DeviceId>> devicePairs = IntStream.
+ range(0, deviceList.size() - 1)
+ .mapToObj(i -> Pair.of((DeviceId) deviceList.get(i), (DeviceId) deviceList.get(i + 1)))
+ .collect(Collectors.toList());
+
+ devicePairs.forEach(pair -> {
+ //TODO use GetPath pair by pair?
+ // The common Link between DevEgress and DevIngress is the intersection of their links
+ Set<Link> commonLinks = new HashSet<>(linkService.getDeviceEgressLinks(pair.getLeft()));
+ commonLinks.retainAll(linkService.getDeviceIngressLinks(pair.getRight()));
+ if (commonLinks.size() == 0) {
+ log.error("No link found between node {} and node {}!",
+ pair.getLeft(), pair.getRight());
+ } else if (commonLinks.size() == 1) {
+ path.add(commonLinks.iterator().next());
+ } else {
+ //TODO select the one with more bandwidth?
+ log.warn("{} links found between node {} and node {}: taking the first one!",
+ commonLinks.size(), pair.getLeft(), pair.getRight());
+ path.add(commonLinks.iterator().next());
+ }
+ });
+
+ return path;
+ }
+
+ public Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> getMonitoredIntents() {
+ Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> currentMonitoredIntents
+ = new ConcurrentHashMap<>();
+ monitoredIntents.forEach((appId, appIntents) -> {
+ currentMonitoredIntents.put(appId, new ConcurrentHashMap<>());
+ appIntents.forEach((intentKey, intent) -> {
+ Pair<Set<ElementId>, Set<ElementId>> endPair = extractEndPoints(intent);
+ if (endPair != null) {
+ currentMonitoredIntents.get(appId).put(intentKey, endPair);
+ }
+ });
+ });
+ return currentMonitoredIntents;
+ }
+
+ public Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> getMonitoredIntents(
+ ApplicationId appId) {
+ Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> currentMonitoredIntents
+ = new ConcurrentHashMap<>();
+ currentMonitoredIntents.put(appId, new ConcurrentHashMap<>());
+ if (monitoredIntents.containsKey(appId)) {
+ monitoredIntents.get(appId).forEach((intentKey, intent) -> {
+ Pair<Set<ElementId>, Set<ElementId>> endPair = extractEndPoints(intent);
+ if (endPair != null) {
+ currentMonitoredIntents.get(appId).put(intentKey, endPair);
+ }
+ });
+ }
+ return currentMonitoredIntents;
+ }
+
+ private Set<ElementId> connectedElements(Set<FilteredConnectPoint> cpSet) {
+ Set<ElementId> connectedElem = new HashSet<>();
+ cpSet.forEach(
+ fcp -> {
+ Set<Host> connectedHosts = hostService.getConnectedHosts(fcp.connectPoint());
+ if (connectedHosts.size() == 0) {
+ // In this case the end point is an ELEMENT without host connected
+ connectedElem.add(fcp.connectPoint().elementId());
+ } else {
+ // In this case we can have a set of hosts connected to that endpoint
+ connectedElem.addAll(connectedHosts.stream().map(Host::id)
+ .collect(Collectors.toSet()));
+ }
+ }
+ );
+ return connectedElem;
+ }
+
+ /**
+ * Extracts the endpoint from an intent.
+ * @param intent
+ * @return {@link Pair} containing in the Left element the set of input {@link ElementId},
+ * in the Right element the set of output {@link ElementId}.
+ */
+ private Pair<Set<ElementId>, Set<ElementId>> extractEndPoints(Intent intent) {
+ checkNotNull(intent, "intent must not be null");
+ Pair<Set<FilteredConnectPoint>, Set<FilteredConnectPoint>> cpPair;
+ cpPair = extractEndConnectPoints(intent);
+ if (cpPair == null) {
+ return null;
+ }
+ return Pair.of(connectedElements(cpPair.getLeft()), connectedElements(cpPair.getRight()));
+ }
+
+ /**
+ * Returns the end connect points of an intent.
+ * @param intent
+ * @return {@link Pair} containing in the Left element the input end connect points,
+ * in the Right element the output end connect points.
+ */
+ private Pair<Set<FilteredConnectPoint>, Set<FilteredConnectPoint>> extractEndConnectPoints(Intent intent) {
+ checkNotNull(intent, "intent must not be null");
+
+ Set<FilteredConnectPoint> inSet = new HashSet<>();
+ Set<FilteredConnectPoint> outSet = new HashSet<>();
+ if (intent instanceof PointToPointIntent) {
+ inSet.add(((PointToPointIntent) intent).filteredIngressPoint());
+ outSet.add(((PointToPointIntent) intent).filteredEgressPoint());
+ } else if (intent instanceof LinkCollectionIntent) {
+ inSet.addAll(((LinkCollectionIntent) intent).filteredIngressPoints());
+ outSet.addAll(((LinkCollectionIntent) intent).filteredEgressPoints());
+ }
+ return Pair.of(inSet, outSet);
+ }
+
+ /**
+ * Returns the connect point related to the output port of the rule.
+ * @param rule
+ * @return
+ */
+ private ConnectPoint buildConnectPoint(FlowRule rule) {
+ PortNumber port = getOutput(rule);
+
+ if (port == null) {
+ return null;
+ }
+ return new ConnectPoint(rule.deviceId(), port);
+ }
+
+ /**
+ * Returns the output port related to the rule.
+ * @param rule
+ * @return
+ */
+ private PortNumber getOutput(FlowRule rule) {
+ for (Instruction i : rule.treatment().allInstructions()) {
+ if (i.type() == Instruction.Type.OUTPUT) {
+ Instructions.OutputInstruction out = (Instructions.OutputInstruction) i;
+ return out.port();
+ }
+ }
+ return null;
+ }
+
+
+ private class InternalIntentListener implements IntentListener {
+
+ @Override
+ public void event(IntentEvent event) {
+ // It receives only events related to ConnectivityIntent to be monitored
+ Key intentKey = event.subject().key();
+ switch (event.type()) {
+ case INSTALLED:
+ // When an intent is installed and it need to be monitored
+ // it will pass from the "toBeMonitored" state to the "monitored" state
+ log.info("Monitored intent INSTALLED");
+ storeMonitoredIntent((ConnectivityIntent) event.subject());
+ break;
+
+ case WITHDRAWN:
+ // When an intent is withdrawn
+ // it will go back from the "monitored" state to the "toBeMonitored"
+ log.info("Monitored intent WITHDWRAWN");
+ removeIntent(intentKey);
+ break;
+
+ case FAILED:
+ log.warn("FAILED event not handled");
+ break;
+ default:
+ log.warn("Unknown intent event");
+ }
+ }
+
+ @Override
+ public boolean isRelevant(IntentEvent event) {
+ /*
+ * Check if the Intent event is relevant.
+ * An intent event is relevant if it is of one of the allowed types
+ * and if it is one of the monitored ones.
+ */
+ Key intentKey = event.subject().key();
+ return allowedIntent(event.subject())
+ && toBeMonitoredIntents.contains(intentKey);
+ }
+ }
+
+ private class InternalFlowRuleListener implements FlowRuleListener {
+ @Override
+ public void event(FlowRuleEvent event) {
+ FlowRule rule = event.subject();
+ switch (event.type()) {
+ case RULE_ADDED:
+ case RULE_UPDATED:
+ // In case of rule update, flow statistics are updated
+ if (rule instanceof FlowEntry) {
+ statsStore.updateFlowStatistic((FlowEntry) rule);
+ }
+ break;
+ case RULE_REMOVED:
+ // In case of rule removal, flow statistics are removed from the store
+ log.info("Rule removed: {}", rule.id());
+ statsStore.removeFlowStatistic(rule);
+ break;
+ default:
+ log.warn("Unknown flow rule event");
+ }
+ }
+
+ @Override
+ public boolean isRelevant(FlowRuleEvent event) {
+ /*
+ * Check if the rule event is relevant and it needs to be managed
+ * A Rule event is relevant if the flow rule it refers to is
+ * part of one of the monitored intents
+ */
+ FlowRule rule = event.subject();
+ for (Map.Entry<ApplicationId, Map<Key, ConnectivityIntent>> entry : monitoredIntents.entrySet()) {
+ for (Key key : entry.getValue().keySet()) {
+ List<Intent> ints = intentService.getInstallableIntents(key);
+ for (Intent i : ints) {
+ if (i instanceof FlowRuleIntent
+ && ((FlowRuleIntent) i).flowRules().contains(rule)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/IntentMonitorAndRerouteService.java b/apps/imr/app/src/main/java/org/onosproject/imr/IntentMonitorAndRerouteService.java
new file mode 100644
index 0000000..b674149
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/IntentMonitorAndRerouteService.java
@@ -0,0 +1,93 @@
+/*
+ * 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.imr;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.imr.data.Route;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.intent.Key;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Intent Monitor and Reroute ONOS service.
+ */
+public interface IntentMonitorAndRerouteService {
+
+ /**
+ * Starts to monitor an intent.
+ * If the intent is not already submitted to the intent subsystem
+ * it memorizes the key and it will start to monitor it as soon as it will be installed
+ * @param intentKey Key of the intent to monitor
+ * @return true, false only if the intent is of one of the not currently supported type
+ */
+ boolean startMonitorIntent(Key intentKey);
+
+ /**
+ * Stops to monitor an intent.
+ * @param intentKey Key of the intent you want to stop the monitoring.
+ * @return false if the intent key passed is not one of the tracked intent, true otherwise.
+ */
+ boolean stopMonitorIntent(Key intentKey);
+
+ /**
+ * Applies a new route to a monitored intent.
+ * @param route Route you want to apply.
+ * @return False in case Application ID or Intent Key are not tracked by IMR
+ */
+ boolean applyPath(Route route);
+
+ /**
+ * Returns the statistics of all the monitored intents.
+ * @return Statistics (in terms of flow entries) of all the monitored intents.
+ */
+ Map<ApplicationId, Map<Key, List<FlowEntry>>> getStats();
+
+ /**
+ * Returns the statistics of all the monitored intents submitted by a specific application.
+ * @param appId Application id of the monitored intent.
+ * @return Statistics (in terms of flow entries) of the requested intents.
+ */
+ Map<ApplicationId, Map<Key, List<FlowEntry>>> getStats(ApplicationId appId);
+
+ /**
+ * Returns the statistics of a specific monitored intent.
+ * @param appId Application id of the monitored intent.
+ * @param intentKey key of the monitored intent.
+ * @return Statistics (in terms of flow entries) of the requested intent.
+ */
+ Map<ApplicationId, Map<Key, List<FlowEntry>>> getStats(ApplicationId appId, Key intentKey);
+
+ /**
+ * Returns the monitored intents in terms of key and connect points.
+ * @return Intents monitored identified by the application id and
+ * the intent key, plus the endpoints of that intent.
+ */
+ Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> getMonitoredIntents();
+
+ /**
+ * Returns the monitored intents submitted by a specific application.
+ * @param appId Application id of the application to extract the monitored intents.
+ * @return Intents monitored identified by the application id and
+ * the intent key, plus the endpoints of that intent.
+ */
+ Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> getMonitoredIntents(ApplicationId appId);
+}
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/data/Path.java b/apps/imr/app/src/main/java/org/onosproject/imr/data/Path.java
new file mode 100644
index 0000000..97de631
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/data/Path.java
@@ -0,0 +1,96 @@
+/*
+ * 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.imr.data;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.MoreObjects;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.HostId;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Representation of a path in terms of list of elements that it has to traverse
+ * and weight.
+ */
+public class Path implements Comparable {
+ private List<ElementId> path;
+ private float weight;
+
+ /**
+ * Returns the list of elements composing the path.
+ * @return the actual path
+ */
+ public List<ElementId> path() {
+ return path;
+ }
+
+ /**
+ * Returns the weight related to the path.
+ * @return the weight
+ */
+ public float weight() {
+ return weight;
+ }
+
+ /**
+ * Creates a Path using Jackson from a JSON Object.
+ * @param path List of element id representig the path.
+ * @param weight Weight related to the path.
+ */
+ @JsonCreator
+ public Path(@JsonProperty("path") List<String> path,
+ @JsonProperty("weight") float weight) {
+ this.path = new ArrayList<>();
+
+ path.forEach(deviceName -> {
+ try {
+ this.path.add((HostId.hostId(deviceName)));
+ } catch (IllegalArgumentException e) {
+ this.path.add(DeviceId.deviceId(deviceName));
+ }
+ });
+ this.weight = weight;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("Path", this.path())
+ .toString();
+ }
+
+ /**
+ * Compare two paths in terms of weight.
+ * @param o Object to compare
+ * @return the comparison result
+ */
+ @Override
+ public int compareTo(Object o) {
+ Path s = (Path) o;
+ if (this.weight < s.weight) {
+ return -1;
+ }
+ if (this.weight > s.weight) {
+ return 1;
+ }
+ return 0;
+ }
+}
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/data/Route.java b/apps/imr/app/src/main/java/org/onosproject/imr/data/Route.java
new file mode 100644
index 0000000..d39f2aee
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/data/Route.java
@@ -0,0 +1,87 @@
+/*
+ * 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.imr.data;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.MoreObjects;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.net.intent.Key;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Representation of a route submitted by the off-platform application
+ * to be applied to an existing intent.
+ * It is composed by the key and the application id of the intent to modify
+ * and a list of possible {@link Path}.
+ */
+public class Route {
+ private Key key;
+ private ApplicationId appId;
+ private List<Path> paths;
+
+ /**
+ * Returns the intent key the route refers to.
+ * @return the intent key
+ */
+ public Key key() {
+ return key;
+ }
+
+ /**
+ * Returns the Application ID of the intent that has to be modified.
+ * @return the Application ID
+ */
+ public ApplicationId appId() {
+ return appId;
+ }
+
+ /**
+ * Returns the list of the {@link Path} on which the intent has to be routed.
+ * @return the list of path
+ */
+ public List<Path> paths() {
+ return paths;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("IntentKey", this.key)
+ .add("ApplicationId", this.appId)
+ .add("Paths", this.paths)
+ .toString();
+ }
+
+ /**
+ * Creates the route using Jackson from a JSON Object.
+ * @param iKey the intent key
+ * @param appId application id
+ * @param paths list of paths
+ */
+ @JsonCreator
+ public Route(@JsonProperty("key") String iKey,
+ @JsonProperty("appId") Map<String, String> appId,
+ @JsonProperty("paths") List<Path> paths) {
+ this.paths = paths;
+ this.appId = new DefaultApplicationId(Integer.valueOf(appId.get("id")), appId.get("name"));
+ this.key = Key.of(iKey, this.appId);
+ }
+}
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/data/RoutingConfigurations.java b/apps/imr/app/src/main/java/org/onosproject/imr/data/RoutingConfigurations.java
new file mode 100644
index 0000000..6d955d1
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/data/RoutingConfigurations.java
@@ -0,0 +1,44 @@
+/*
+ * 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.imr.data;
+
+import com.google.common.base.MoreObjects;
+
+import java.util.List;
+
+/**
+ * Representation of the routing confiuration submitted by the off-platform application
+ * It is composed by a list of {@link Route}.
+ */
+public class RoutingConfigurations {
+ public List<Route> routingList;
+
+ /**
+ * Returns the list of all the routing submitted by the off-platform application.
+ * @return the routing list
+ */
+ public List<Route> routingList() {
+ return routingList;
+ }
+
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("RoutingList", this.routingList)
+ .toString();
+ }
+
+}
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/data/package-info.java b/apps/imr/app/src/main/java/org/onosproject/imr/data/package-info.java
new file mode 100644
index 0000000..961cc1d
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/data/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Data support classes for Intent Monitor and Reroute.
+ */
+package org.onosproject.imr.data;
\ No newline at end of file
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/package-info.java b/apps/imr/app/src/main/java/org/onosproject/imr/package-info.java
new file mode 100644
index 0000000..1a5e154
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014-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.
+ */
+
+/**
+ * Intent Monitor and Reroute application.
+ */
+package org.onosproject.imr;