IntentMonitorAndReroute initial contribution
Change-Id: I88616235b1e8ae28894da75b3fc8d46cb209dac5
diff --git a/apps/imr/api/BUCK b/apps/imr/api/BUCK
new file mode 100644
index 0000000..a6a3885
--- /dev/null
+++ b/apps/imr/api/BUCK
@@ -0,0 +1,22 @@
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+ '//lib:JACKSON',
+ '//lib:javax.ws.rs-api',
+ '//utils/rest:onlab-rest',
+ '//lib:jersey-server',
+ '//core/store/serializers:onos-core-serializers',
+ '//apps/imr/app:onos-apps-imr-app',
+ '//cli:onos-cli',
+ '//lib:org.apache.karaf.shell.console',
+]
+
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+ web_context = '/onos/v1/imr',
+ api_title = 'IMR REST API',
+ api_version = '1.0',
+ api_description = 'REST API for IMR Application',
+ api_package = 'org.onosproject.imr.rest',
+)
+
diff --git a/apps/imr/api/pom.xml b/apps/imr/api/pom.xml
new file mode 100644
index 0000000..590342a
--- /dev/null
+++ b/apps/imr/api/pom.xml
@@ -0,0 +1,112 @@
+<?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-imr</artifactId>
+ <version>1.13.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-app-imr-api</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>Intent Monitoring and Rerouting application REST</description>
+
+ <properties>
+ <web.context>/onos/v1/imr</web.context>
+ <api.version>1.0.0</api.version>
+ <api.title>IMR Server REST API</api.title>
+ <api.description>
+ APIs for interacting with the IMR application.
+ </api.description>
+ <api.package>org.onosproject.imr.rest</api.package>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-imr-app</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>javax.ws.rs-api</artifactId>
+ <version>2.0.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+
+ <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.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-servlet-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>1.13.0-SNAPSHOT</version>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/cli/ApplicationIdImrCompleter.java b/apps/imr/api/src/main/java/org/onosproject/imr/cli/ApplicationIdImrCompleter.java
new file mode 100644
index 0000000..010c133
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/cli/ApplicationIdImrCompleter.java
@@ -0,0 +1,49 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.intent.IntentService;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * Application ID completer.
+ */
+public class ApplicationIdImrCompleter implements Completer {
+
+ @Override
+ public int complete(String buffer, int cursor, List<String> candidates) {
+ // Delegate string completer
+ StringsCompleter delegate = new StringsCompleter();
+
+ // Fetch our service and feed it's offerings to the string completer
+ IntentService service = AbstractShellCommand.get(IntentService.class);
+ SortedSet<String> strings = delegate.getStrings();
+
+ service.getIntents()
+ .forEach(intent ->
+ strings.add(Short.toString(intent.appId().id())));
+
+ // Now let the completer do the work for figuring out what to offer.
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+}
\ No newline at end of file
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/cli/ApplicationNameImrCompleter.java b/apps/imr/api/src/main/java/org/onosproject/imr/cli/ApplicationNameImrCompleter.java
new file mode 100644
index 0000000..d5f7bd4
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/cli/ApplicationNameImrCompleter.java
@@ -0,0 +1,48 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.intent.IntentService;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * Application name completer.
+ */
+public class ApplicationNameImrCompleter implements Completer {
+ @Override
+ public int complete(String buffer, int cursor, List<String> candidates) {
+ // Delegate string completer
+ StringsCompleter delegate = new StringsCompleter();
+
+ // Fetch our service and feed it's offerings to the string completer
+ IntentService service = AbstractShellCommand.get(IntentService.class);
+ SortedSet<String> strings = delegate.getStrings();
+
+ service.getIntents()
+ .forEach(intent ->
+ strings.add(intent.appId().name()));
+
+ // Now let the completer do the work for figuring out what to offer.
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+}
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/cli/IntentKeyImrCompleter.java b/apps/imr/api/src/main/java/org/onosproject/imr/cli/IntentKeyImrCompleter.java
new file mode 100644
index 0000000..8b5b150
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/cli/IntentKeyImrCompleter.java
@@ -0,0 +1,55 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+
+import java.util.List;
+import java.util.SortedSet;
+
+
+/**
+ * Intent Key completer for IMR cli command.
+ */
+public class IntentKeyImrCompleter implements Completer {
+
+ @Override
+ public int complete(String buffer, int cursor, List<String> candidates) {
+ // Delegate string completer
+ StringsCompleter delegate = new StringsCompleter();
+
+ // Fetch our service and feed it's offerings to the string completer
+ IntentService service = AbstractShellCommand.get(IntentService.class);
+ SortedSet<String> strings = delegate.getStrings();
+ service.getIntents().forEach(intent -> {
+ if (intent instanceof LinkCollectionIntent
+ || intent instanceof PointToPointIntent) {
+ strings.add(intent.key().toString());
+ }
+ });
+
+ // Now let the completer do the work for figuring out what to offer.
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+}
+
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/cli/StartMonitorCommand.java b/apps/imr/api/src/main/java/org/onosproject/imr/cli/StartMonitorCommand.java
new file mode 100644
index 0000000..dfba41c
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/cli/StartMonitorCommand.java
@@ -0,0 +1,107 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.imr.IntentMonitorAndRerouteService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
+
+/**
+ * Starts monitoring of an intent submitting its key to the IMR service.
+ */
+@Command(scope = "imr", name = "startmon",
+ description = "Submit an intent to the IMR application to start monitoring")
+public class StartMonitorCommand extends AbstractShellCommand {
+
+ @Option(name = "-l", aliases = "--longkey", description = "Treat intentKey as LongKey",
+ required = false, multiValued = false)
+ private boolean treatAsLongKey = false;
+
+ @Argument(index = 0, name = "applicationId",
+ description = "Application ID that submitted the intent",
+ required = true)
+ private Short appId;
+
+ @Argument(index = 1, name = "applicationName",
+ description = "Application Name that submitted the intent",
+ required = true)
+ private String appName;
+
+ @Argument(index = 2, name = "intentKey",
+ description = "String representation of the key of the intent",
+ required = false)
+ private String key;
+
+ private IntentMonitorAndRerouteService imrService;
+ private IntentService intentService;
+
+ @Override
+ protected void execute() {
+ imrService = get(IntentMonitorAndRerouteService.class);
+ intentService = get(IntentService.class);
+
+ if (appId != null && appName != null) {
+ if (key != null) {
+ /*
+ Intent key might be a StringKey or a LongKey, but in any case is
+ provided via CLI as a string. To solve only ambiguity we check if
+ "--longkey" CLI parameter has been set.
+ */
+ if (treatAsLongKey) {
+ try {
+ Key intentKeyLong = Key.of(Integer.decode(key), new DefaultApplicationId(appId, appName));
+ for (Intent intent : intentService.getIntents()) {
+ if (intent.key().equals(intentKeyLong)) {
+ imrService.startMonitorIntent(intentKeyLong);
+ print("Started monitoring of intent with LongKey %s", intentKeyLong);
+ return;
+ }
+ }
+ imrService.startMonitorIntent(intentKeyLong);
+ print("Started monitoring of intent with LongKey %s, even if not yet submitted", intentKeyLong);
+ } catch (NumberFormatException nfe) {
+ print("\"%s\" is not a valid LongKey", key);
+ }
+ } else {
+ Key intentKeyString = Key.of(key, new DefaultApplicationId(appId, appName));
+ for (Intent intent : intentService.getIntents()) {
+ if (intent.key().equals(intentKeyString)) {
+ imrService.startMonitorIntent(intentKeyString);
+ print("Started monitoring of intent with StringKey %s", intentKeyString);
+ return;
+ }
+ }
+ imrService.startMonitorIntent(intentKeyString);
+ print("Started monitoring of intent with StringKey %s, even if not yet submitted", intentKeyString);
+ }
+ } else {
+ intentService.getIntents().forEach(i -> {
+ if (i.appId().equals(new DefaultApplicationId(appId, appName))) {
+ imrService.startMonitorIntent(i.key());
+ print("Started monitoring of intent with Key %s", i.key());
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/cli/StopMonitorCommand.java b/apps/imr/api/src/main/java/org/onosproject/imr/cli/StopMonitorCommand.java
new file mode 100644
index 0000000..933b4be
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/cli/StopMonitorCommand.java
@@ -0,0 +1,101 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.imr.IntentMonitorAndRerouteService;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
+
+/**
+ * Stops monitoring of an intent by the IMR service.
+ */
+@Command(scope = "imr", name = "stopmon",
+ description = "Stop monitoring and intent already submitted to the IMR")
+public class StopMonitorCommand extends AbstractShellCommand {
+
+ @Option(name = "-l", aliases = "--longkey", description = "Treat intentKey as LongKey",
+ required = false, multiValued = false)
+ private boolean treatAsLongKey = false;
+
+ @Argument(index = 0, name = "applicationId",
+ description = "Application ID that submitted the intent",
+ required = true)
+ private Short appId = null;
+
+ @Argument(index = 1, name = "applicationName",
+ description = "Application Name that submitted the intent",
+ required = true)
+ private String appName = null;
+
+ @Argument(index = 2, name = "intentKey",
+ description = "String representation of the key of the intent",
+ required = false)
+ private String key = null;
+
+ private IntentMonitorAndRerouteService imrService;
+ private IntentService intentService;
+
+ @Override
+ protected void execute() {
+ imrService = get(IntentMonitorAndRerouteService.class);
+ intentService = get(IntentService.class);
+
+ if (appId != null && appName != null) {
+ if (key != null) {
+ /*
+ Intent key might be a StringKey or a LongKey, but in any case is
+ provided via CLI as a string. To solve only ambiguity we check if
+ "--longkey" CLI parameter has been set.
+ */
+ if (treatAsLongKey) {
+ try {
+ Key intentKeyLong = Key.of(Integer.decode(key), new DefaultApplicationId(appId, appName));
+ if (imrService.stopMonitorIntent(intentKeyLong)) {
+ print("Stopped monitoring of intent with LongKey %s", intentKeyLong);
+ return;
+ }
+ } catch (NumberFormatException nfe) {
+ print("\"%s\" is not a valid LongKey", key);
+ return;
+ }
+ } else {
+ Key intentKeyString = Key.of(key, new DefaultApplicationId(appId, appName));
+ if (imrService.stopMonitorIntent(intentKeyString)) {
+ print("Stopped monitoring of intent with StringKey %s", intentKeyString);
+ return;
+ }
+ }
+
+ //No intent found in IMR
+ print("No monitored intent with key %s found", key);
+ } else {
+ intentService.getIntents().forEach(i -> {
+ if (i.appId().equals(new DefaultApplicationId(appId, appName))) {
+ imrService.stopMonitorIntent(i.key());
+ print("Stopped monitoring of intent with key %s", i.key());
+ }
+ });
+ }
+ }
+
+ }
+}
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/cli/package-info.java b/apps/imr/api/src/main/java/org/onosproject/imr/cli/package-info.java
new file mode 100644
index 0000000..4ade153
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/cli/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 cli command.
+ */
+package org.onosproject.imr.cli;
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/rest/ImrWebApplication.java b/apps/imr/api/src/main/java/org/onosproject/imr/rest/ImrWebApplication.java
new file mode 100644
index 0000000..d09e32a
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/rest/ImrWebApplication.java
@@ -0,0 +1,32 @@
+/*
+ * 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.rest;
+
+import org.onlab.rest.AbstractWebApplication;
+
+import java.util.Set;
+
+/**
+ * Intent Monitor and Reroute web application.
+ */
+public class ImrWebApplication extends AbstractWebApplication {
+
+ @Override
+ public Set<Class<?>> getClasses() {
+ return getClasses(ImrWebResource.class);
+ }
+}
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/rest/ImrWebResource.java b/apps/imr/api/src/main/java/org/onosproject/imr/rest/ImrWebResource.java
new file mode 100644
index 0000000..8e717d0
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/rest/ImrWebResource.java
@@ -0,0 +1,273 @@
+/*
+ * 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.rest;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.commons.lang3.tuple.Pair;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.imr.IntentMonitorAndRerouteService;
+import org.onosproject.imr.data.Route;
+import org.onosproject.imr.data.RoutingConfigurations;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.intent.Key;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Intent Monitor and Reroute REST API.
+ */
+@Path("imr")
+public class ImrWebResource extends AbstractWebResource {
+
+ public static final String ROOT_FIELD_STATISTICS_NAME = "statistics";
+ public static final String ROOT_FIELD_MONITORED_INTENTS = "response";
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private IntentMonitorAndRerouteService imrService;
+
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ /**
+ * Get the statistics of the monitored intents.
+ * Shows for each intent all the flow entries.
+ *
+ * @onos.rsModel intentStatsGet
+ * @return 200 OK
+ */
+ @GET
+ @Path("intentStats")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getIntentsStats() {
+ ObjectNode root = mapper.createObjectNode();
+ imrService = get(IntentMonitorAndRerouteService.class);
+ root.putArray(ROOT_FIELD_STATISTICS_NAME).addAll(
+ getJsonNodesIntentStats(imrService.getStats()));
+ return ok(root).build();
+ }
+
+ /**
+ * Get the statistics of the monitored intent of a specific application
+ * Shows for each intent all the flow entries.
+ *
+ * @onos.rsModel intentStatsGet
+ * @param id Application ID
+ * @param name Application Name
+ * @return 200 OK
+ */
+ @GET
+ @Path("intentStats/{id}/{name}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getIntentsStats(@PathParam("id") short id, @PathParam("name") String name) {
+ ObjectNode root = mapper.createObjectNode();
+ imrService = get(IntentMonitorAndRerouteService.class);
+ ApplicationId appId = new DefaultApplicationId(id, name);
+ root.putArray(ROOT_FIELD_STATISTICS_NAME).addAll(
+ getJsonNodesIntentStats(imrService.getStats(appId)));
+ return ok(root).build();
+ }
+
+ /**
+ * Get the statistics of a specific monitored intent.
+ * Shows all the flow entries of the specific intent
+ *
+ * @onos.rsModel intentStatsGet
+ * @param id Application ID
+ * @param name Application Name
+ * @param intentK Intent Key
+ * @return 200 OK
+ */
+ @GET
+ @Path("intentStats/{id}/{name}/{intentKey}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getIntentsStats(@PathParam("id") short id,
+ @PathParam("name") String name,
+ @PathParam("intentKey") String intentK) {
+ ObjectNode root = mapper.createObjectNode();
+ imrService = get(IntentMonitorAndRerouteService.class);
+ ApplicationId appId = new DefaultApplicationId(id, name);
+ Key intentKey = Key.of(intentK, appId);
+ root.putArray("statistics").addAll(
+ getJsonNodesIntentStats(imrService.getStats(appId, intentKey)));
+ return ok(root).build();
+ }
+
+ /**
+ * Build the Json Nodes from the intent stats retrieved from {@link IntentMonitorAndRerouteService}.
+ *
+ * @param mapKeyToStats Intent statistics
+ * @return {@link ArrayNode} built from the statistics
+ */
+ private ArrayNode getJsonNodesIntentStats(Map<ApplicationId, Map<Key, List<FlowEntry>>> mapKeyToStats) {
+ final ArrayNode rootArrayNode = mapper.createArrayNode();
+ mapKeyToStats.forEach((appId, mapIntentKeyStats) -> {
+ ObjectNode appObjNode = codec(ApplicationId.class).encode(appId, this);
+ ArrayNode intentArrayNode = appObjNode.putArray("intents");
+ mapIntentKeyStats.forEach((intentKey, lstStats) -> {
+ ObjectNode intentKeyObjNode = mapper.createObjectNode();
+ ArrayNode statsArrayNode = intentKeyObjNode.putArray(intentKey.toString());
+ lstStats.forEach(stat -> {
+ statsArrayNode.add(codec(FlowEntry.class).encode(stat, this));
+ });
+
+ intentArrayNode.add(intentKeyObjNode);
+ });
+
+ rootArrayNode.add(appObjNode);
+ });
+ return rootArrayNode;
+ }
+
+ /**
+ * Get the list of monitored intents.
+ * Shows for each intent key the related end points (as inElements and OutElements).
+ *
+ * @onos.rsModel monitoredIntentsGet
+ * @return 200 OK
+ */
+ @GET
+ @Path("monitoredIntents")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getMonitoredIntents() {
+ imrService = get(IntentMonitorAndRerouteService.class);
+ ArrayNode jsonMonitoredIntents = getJsonMonitoredIntents(imrService.getMonitoredIntents());
+ ObjectNode result = mapper.createObjectNode();
+ result.putArray(ROOT_FIELD_MONITORED_INTENTS).addAll(jsonMonitoredIntents);
+ return ok(result).build();
+ }
+
+ /**
+ * Get the list of monitored intents of a specific application.
+ * Shows for each intent key the related end points (as inElements and OutElements).
+ *
+ * @onos.rsModel monitoredIntentsGet
+ * @param id Application ID
+ * @param name Application Name
+ * @return 200 OK
+ */
+ @GET
+ @Path("monitoredIntents/{id}/{name}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getMonitoredIntents(@PathParam("id") short id, @PathParam("name") String name) {
+ imrService = get(IntentMonitorAndRerouteService.class);
+ ApplicationId appId = new DefaultApplicationId(id, name);
+ ArrayNode jsonMonitoredIntents = getJsonMonitoredIntents(imrService.getMonitoredIntents(appId));
+ ObjectNode result = mapper.createObjectNode();
+ result.putArray(ROOT_FIELD_MONITORED_INTENTS).addAll(jsonMonitoredIntents);
+ return ok(result).build();
+ }
+
+ /**
+ * Build the JSON Node from the monitored intents retrieved from {@link IntentMonitorAndRerouteService}.
+ *
+ * @param monIntents Monitored intents structure.
+ * @return {@link ArrayNode} built from the monitored intents.
+ */
+ private ArrayNode getJsonMonitoredIntents(
+ Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> monIntents) {
+ final ArrayNode rootArrayNode = mapper.createArrayNode();
+ monIntents.forEach((appId, mapIntentKeyEndElements) -> {
+ ObjectNode appObjNode = codec(ApplicationId.class).encode(appId, this);
+ ArrayNode intentArrayNode = appObjNode.putArray("intents");
+ mapIntentKeyEndElements.forEach((intentKey, inOutElem) -> {
+ ObjectNode intentKeyObjNode = mapper.createObjectNode()
+ .put("key", intentKey.toString());
+ ArrayNode inElements = intentKeyObjNode.putArray("inElements");
+ inOutElem.getLeft().forEach(elementId -> inElements.add(elementId.toString()));
+ ArrayNode outElements = intentKeyObjNode.putArray("outElements");
+ inOutElem.getRight().forEach(elementId -> outElements.add(elementId.toString()));
+ intentArrayNode.add(intentKeyObjNode);
+ });
+
+ rootArrayNode.add(appObjNode);
+ });
+ return rootArrayNode;
+ }
+
+
+ /**
+ * POST a list of routing configurations. For each intents a list of paths
+ * with relative weights is specified. The body of HTTP POST is a JSON
+ *
+ * @onos.rsModel reRouteIntentsPost
+ * @param stream JSON stream
+ * @return 200 OK if the routing configurations are applied correctly,
+ * otherwise 500 Internal Server Error
+ */
+ @POST
+ @Path("reRouteIntents")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response reRouteIntents(InputStream stream) {
+ imrService = get(IntentMonitorAndRerouteService.class);
+ ObjectNode result = mapper().createObjectNode();
+ StringBuilder resultString = new StringBuilder();
+
+ mapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ try {
+ RoutingConfigurations msg = mapper().readValue(stream, RoutingConfigurations.class);
+ for (Route routingConfiguration: msg.routingList()) {
+ String outcome;
+ try {
+ if (!imrService.applyPath(routingConfiguration)) {
+ outcome = "Application ID:" + routingConfiguration.appId()
+ + " or Intent Key:" + routingConfiguration.key()
+ + " not monitored!";
+ } else {
+ outcome = "OK";
+ }
+ } catch (IllegalArgumentException | NullPointerException ex) {
+ outcome = ex.getMessage();
+ }
+ if (!outcome.equals("OK")) {
+ if (resultString.length() > 0) {
+ resultString.append(" ");
+ }
+ resultString.append(outcome);
+ }
+ }
+ if (resultString.length() > 0) {
+ result.put("response", "addRouting() failed: ".concat(resultString.toString()));
+ } else {
+ result.put("response", "OK");
+ }
+ return ok(result).build();
+ } catch (Exception e) {
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).
+ entity(e.toString())
+ .build();
+ }
+ }
+}
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/rest/package-info.java b/apps/imr/api/src/main/java/org/onosproject/imr/rest/package-info.java
new file mode 100644
index 0000000..bdd5740
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/rest/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.
+ */
+
+/**
+ * Intent Monitor and Reroute application.
+ */
+package org.onosproject.imr.rest;
diff --git a/apps/imr/api/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/imr/api/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..fdddc88
--- /dev/null
+++ b/apps/imr/api/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,29 @@
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+
+ <command>
+ <action class="org.onosproject.imr.cli.StartMonitorCommand"/>
+ <completers>
+ <ref component-id="applicationIdCompleter"/>
+ <ref component-id="applicationNameCompleter"/>
+ <ref component-id="intentKeyImrCompleter"/>
+ </completers>
+ </command>
+
+ <command>
+ <action class="org.onosproject.imr.cli.StopMonitorCommand"/>
+ <completers>
+ <ref component-id="applicationIdCompleter"/>
+ <ref component-id="applicationNameCompleter"/>
+ <ref component-id="intentKeyImrCompleter"/>
+ </completers>
+ </command>
+
+ </command-bundle>
+
+ <bean id="applicationIdCompleter" class="org.onosproject.imr.cli.ApplicationIdImrCompleter"/>
+ <bean id="applicationNameCompleter" class="org.onosproject.imr.cli.ApplicationNameImrCompleter"/>
+ <bean id="intentKeyImrCompleter" class="org.onosproject.imr.cli.IntentKeyImrCompleter"/>
+
+</blueprint>
diff --git a/apps/imr/api/src/main/resources/definitions/intentStatsGet.json b/apps/imr/api/src/main/resources/definitions/intentStatsGet.json
new file mode 100644
index 0000000..5e60ccb
--- /dev/null
+++ b/apps/imr/api/src/main/resources/definitions/intentStatsGet.json
@@ -0,0 +1,96 @@
+{
+ "statistics": [
+ {
+ "id": 71,
+ "name": "org.onosproject.ifwd",
+ "intents": [
+ {
+ "00:00:00:00:00:08/None00:00:00:00:00:01/None": [
+ {
+ "id": "41658299096955970",
+ "tableId": 0,
+ "appId": "org.onosproject.net.intent",
+ "groupId": 0,
+ "priority": 100,
+ "timeout": 0,
+ "isPermanent": true,
+ "deviceId": "of:000000000000000c",
+ "state": "ADDED",
+ "life": 12,
+ "packets": 12,
+ "bytes": 1176,
+ "liveType": "UNKNOWN",
+ "lastSeen": 1512041486127,
+ "treatment": {
+ "instructions": [
+ {
+ "type": "OUTPUT",
+ "port": "2"
+ }
+ ],
+ "deferred": []
+ },
+ "selector": {
+ "criteria": [
+ {
+ "type": "IN_PORT",
+ "port": 4
+ },
+ {
+ "type": "ETH_DST",
+ "mac": "00:00:00:00:00:01"
+ },
+ {
+ "type": "ETH_SRC",
+ "mac": "00:00:00:00:00:08"
+ }
+ ]
+ }
+ },
+ {
+ "id": "41658299401587474",
+ "tableId": 0,
+ "appId": "org.onosproject.net.intent",
+ "groupId": 0,
+ "priority": 100,
+ "timeout": 0,
+ "isPermanent": true,
+ "deviceId": "of:0000000000000002",
+ "state": "ADDED",
+ "life": 12,
+ "packets": 12,
+ "bytes": 1176,
+ "liveType": "UNKNOWN",
+ "lastSeen": 1512041486122,
+ "treatment": {
+ "instructions": [
+ {
+ "type": "OUTPUT",
+ "port": "2"
+ }
+ ],
+ "deferred": []
+ },
+ "selector": {
+ "criteria": [
+ {
+ "type": "IN_PORT",
+ "port": 3
+ },
+ {
+ "type": "ETH_DST",
+ "mac": "00:00:00:00:00:01"
+ },
+ {
+ "type": "ETH_SRC",
+ "mac": "00:00:00:00:00:08"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/apps/imr/api/src/main/resources/definitions/monitoredIntentsGet.json b/apps/imr/api/src/main/resources/definitions/monitoredIntentsGet.json
new file mode 100644
index 0000000..c577adc
--- /dev/null
+++ b/apps/imr/api/src/main/resources/definitions/monitoredIntentsGet.json
@@ -0,0 +1,28 @@
+{
+ "response": [
+ {
+ "id": 71,
+ "name": "org.onosproject.ifwd",
+ "intents": [
+ {
+ "key": "00:00:00:00:00:08/None00:00:00:00:00:01/None",
+ "inElements": [
+ "00:00:00:00:00:08/None"
+ ],
+ "outElements": [
+ "00:00:00:00:00:01/None"
+ ]
+ },
+ {
+ "key": "00:00:00:00:00:01/None00:00:00:00:00:08/None",
+ "inElements": [
+ "00:00:00:00:00:01/None"
+ ],
+ "outElements": [
+ "00:00:00:00:00:08/None"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/apps/imr/api/src/main/resources/definitions/reRouteIntentsPost.json b/apps/imr/api/src/main/resources/definitions/reRouteIntentsPost.json
new file mode 100644
index 0000000..e6c088b
--- /dev/null
+++ b/apps/imr/api/src/main/resources/definitions/reRouteIntentsPost.json
@@ -0,0 +1,44 @@
+{
+ "routingList": [
+ {
+ "key": "00:00:00:00:00:08/None00:00:00:00:00:01/None",
+ "appId": {
+ "id" : 71,
+ "name" : "org.onosproject.ifwd"
+ },
+ "paths": [
+ {
+ "path":
+ ["00:00:00:00:00:08/None",
+ "of:000000000000000c",
+ "of:0000000000000001",
+ "of:000000000000000d",
+ "of:0000000000000002",
+ "of:000000000000000b",
+ "00:00:00:00:00:01/None"],
+ "weight": 1.0
+ }
+ ]
+ },
+ {
+ "key": "00:00:00:00:00:01/None00:00:00:00:00:08/None",
+ "appId": {
+ "id" : 71,
+ "name" : "org.onosproject.ifwd"
+ },
+ "paths": [
+ {
+ "path":
+ ["00:00:00:00:00:01/None",
+ "of:000000000000000b",
+ "of:0000000000000002",
+ "of:000000000000000e",
+ "of:0000000000000001",
+ "of:000000000000000c",
+ "00:00:00:00:00:01/None"],
+ "weight": 1.0
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/apps/imr/api/src/main/webapp/WEB-INF/web.xml b/apps/imr/api/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..ce093c0
--- /dev/null
+++ b/apps/imr/api/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,58 @@
+<?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.
+ -->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ id="ONOS" version="2.5">
+ <display-name>Intent Monitor and Reroute REST API</display-name>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Secured</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>admin</role-name>
+ </auth-constraint>
+ </security-constraint>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+
+ <login-config>
+ <auth-method>BASIC</auth-method>
+ <realm-name>karaf</realm-name>
+ </login-config>
+
+ <servlet>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+ <init-param>
+ <param-name>javax.ws.rs.Application</param-name>
+ <param-value>org.onosproject.imr.rest.ImrWebApplication</param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+</web-app>