ONOS-5504: Added REST API for administering OFAgent.
Change-Id: Idc9a18fc82ec23fbb8348fe213f8007e362253ac
diff --git a/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/OFAgentCodec.java b/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/OFAgentCodec.java
new file mode 100644
index 0000000..6e6e772
--- /dev/null
+++ b/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/OFAgentCodec.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.ofagent.rest;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Sets;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.ofagent.api.OFAgent;
+import org.onosproject.ofagent.api.OFController;
+import org.onosproject.ofagent.impl.DefaultOFAgent;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * OpenFlow agent JSON codec.
+ */
+public final class OFAgentCodec extends JsonCodec<OFAgent> {
+
+ @Override
+ public ObjectNode encode(OFAgent ofAgent, CodecContext context) {
+ checkNotNull(ofAgent, "OFAgent cannot be null");
+
+ ObjectMapper mapper = context.mapper();
+ ObjectNode ofAgentNode = mapper.createObjectNode();
+ ofAgentNode
+ .put("networkId", ofAgent.networkId().toString())
+ .put("state", ofAgent.state().toString());
+
+ ArrayNode controllers = mapper.createArrayNode();
+ ofAgent.controllers().forEach(ofController -> controllers.add((new OFControllerCodec()).encode(ofController,
+ context)));
+ ofAgentNode.set("controllers", controllers);
+
+ return ofAgentNode;
+ }
+
+ public OFAgent decode(ObjectNode json, CodecContext context) {
+ JsonNode networkId = json.get("networkId");
+ checkNotNull(networkId);
+
+ checkNotNull(json.get("controllers"));
+ checkState(json.get("controllers").isArray());
+ Set<OFController> controllers = Sets.newHashSet();
+ json.get("controllers").forEach(jsonController -> controllers.add((new
+ OFControllerCodec()).decode((ObjectNode) jsonController, context)));
+
+ return DefaultOFAgent.builder()
+ .networkId(NetworkId.networkId(networkId.asLong()))
+ .controllers(controllers)
+ .state(OFAgent.State.STOPPED)
+ .build();
+ }
+
+}
diff --git a/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/OFAgentWebApplication.java b/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/OFAgentWebApplication.java
new file mode 100644
index 0000000..173e1cd
--- /dev/null
+++ b/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/OFAgentWebApplication.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.ofagent.rest;
+
+import org.onlab.rest.AbstractWebApplication;
+
+import java.util.Set;
+
+
+/**
+ * OFAgent Web application.
+ */
+public class OFAgentWebApplication extends AbstractWebApplication {
+ @Override
+ public Set<Class<?>> getClasses() {
+ return getClasses(OFAgentWebResource.class);
+ }
+}
diff --git a/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/OFAgentWebResource.java b/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/OFAgentWebResource.java
new file mode 100644
index 0000000..58d22a1
--- /dev/null
+++ b/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/OFAgentWebResource.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.ofagent.rest;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.ofagent.api.OFAgent;
+import org.onosproject.ofagent.api.OFAgentAdminService;
+import org.onosproject.ofagent.api.OFAgentService;
+import org.onosproject.rest.AbstractWebResource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import static javax.ws.rs.core.Response.Status.*;
+
+
+/**
+ * Manage virtual switch and controller mapping.
+ */
+@Path("service")
+public class OFAgentWebResource extends AbstractWebResource {
+
+ private static final String OFAGENT_NOT_FOUND = "OFAgent not found";
+ private static final String OFAGENTS_NOT_FOUND = "OFAgent set not found";
+ private static final String OFAGENT_CREATED = "OFAgent created";
+ private static final String OFAGENT_NOT_CREATED = "OFAgent not created";
+ private static final String OFAGENT_STARTED = "OFAgent started";
+ private static final String OFAGENT_NOT_STARTED = "OFAgent not started";
+ private static final String OFAGENT_UPDATED = "OFAgent updated";
+ private static final String OFAGENT_NOT_UPDATED = "OFAgent not updated";
+
+ /**
+ * Lists OpenFlow agents.
+ * Shows OpenFlow agents for all virtual networks.
+ *
+ * @return 200 OK if set exists, 500 INTERNAL SERVER ERROR otherwise
+ */
+ @GET
+ @Path("ofagents")
+ public Response listOFAgents() {
+ OFAgentService service = get(OFAgentService.class);
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode root = mapper.createObjectNode();
+ ArrayNode ofAgentsArray = mapper.createArrayNode();
+ if (service.agents() == null) {
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity(OFAGENTS_NOT_FOUND).build();
+ } else {
+ service.agents().forEach(ofAgent -> ofAgentsArray.add((new OFAgentCodec()).encode(ofAgent, this)));
+
+ root.set("ofAgents", ofAgentsArray);
+ return Response.ok(root, MediaType.APPLICATION_JSON_TYPE).build();
+ }
+
+ }
+
+ /**
+ * Lists OpenFlow agent.
+ * Shows OpenFlow agent for given network.
+ *
+ * @param networkId OFAgent networkId
+ * @return 200 OK if OFAgent exists, 404 NOT FOUND otherwise
+ */
+ @GET
+ @Path("ofagent/{networkId}")
+ public Response listOFAgent(@PathParam("networkId") long networkId) {
+ OFAgentService service = get(OFAgentService.class);
+ OFAgent ofAgent = service.agent(NetworkId.networkId(networkId));
+ if (ofAgent == null) {
+ return Response.status(NOT_FOUND)
+ .entity(OFAGENT_NOT_FOUND).build();
+ } else {
+ return Response.ok((new OFAgentCodec()).encode(ofAgent, this), MediaType
+ .APPLICATION_JSON_TYPE)
+ .build();
+ }
+ }
+
+ /**
+ * Adds a new OpenFlow agent.
+ * Creates a new OpenFlow agent and adds it to OpenFlow agent store.
+ *
+ * @param stream JSON stream
+ * @return 201 CREATED , 400 BAD REQUEST if stream cannot be decoded to OFAgent
+ * @throws IOException if request cannot be parsed
+ */
+ @POST
+ @Path("ofagent-create")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response createOFAgent(InputStream stream) throws IOException {
+ OFAgentAdminService adminService = get(OFAgentAdminService.class);
+
+ OFAgent ofAgent = (new OFAgentCodec()).decode((ObjectNode) mapper().readTree(stream), this);
+ if (ofAgent == null) {
+ return Response.status(BAD_REQUEST)
+ .entity(OFAGENT_NOT_CREATED).build();
+ } else {
+ adminService.createAgent(ofAgent);
+ return Response.status(CREATED).entity(OFAGENT_CREATED).build();
+ }
+ }
+
+ /**
+ * Starts OpenFlow agent.
+ * Starts OpenFlow agent for the given network.
+ *
+ * @param stream JSON stream
+ * @return 200 OK if OFAgent was started, 404 NOT FOUND when OF agent does not exist, 400 BAD REQUEST otherwise
+ * @throws IOException if request cannot be parsed
+ */
+ @POST
+ @Path("ofagent-start")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response startOFAgent(InputStream stream) throws IOException {
+ OFAgentAdminService adminService = get(OFAgentAdminService.class);
+
+ ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
+ JsonNode networkId = jsonTree.get("networkId");
+
+ if (networkId == null) {
+ return Response.status(BAD_REQUEST)
+ .entity(OFAGENT_NOT_STARTED).build();
+ } else if (get(OFAgentService.class).agent(NetworkId.networkId(networkId.asLong())) == null) {
+ return Response.status(NOT_FOUND)
+ .entity(OFAGENT_NOT_STARTED).build();
+ } else {
+ adminService.startAgent(NetworkId.networkId(networkId.asLong()));
+ return Response.status(OK).entity(OFAGENT_STARTED).build();
+ }
+ }
+
+ /**
+ * Updates OpenFlow agent.
+ * Updates existing OpenFlow agent for the given network.
+ *
+ * @param stream JSON stream
+ * @return 200 OK if OFAgent was updated, 404 NOT FOUND when OF agent does not exist, 400 BAD REQUEST otherwise
+ * @throws IOException if request cannot be parsed
+ */
+ @PUT
+ @Path("ofagent-update")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response updateOFAgent(InputStream stream) throws IOException {
+ OFAgentAdminService adminService = get(OFAgentAdminService.class);
+
+ OFAgent ofAgent = (new OFAgentCodec()).decode((ObjectNode) mapper().readTree(stream), this);
+
+ if (ofAgent == null) {
+ return Response.status(NOT_FOUND)
+ .entity(OFAGENT_NOT_UPDATED).build();
+ } else if (get(OFAgentService.class).agent(ofAgent.networkId()) == null) {
+ return Response.status(NOT_FOUND)
+ .entity(OFAGENT_NOT_UPDATED).build();
+ }
+
+ adminService.updateAgent(ofAgent);
+ return Response.status(OK).entity(OFAGENT_UPDATED).build();
+ }
+
+
+ /**
+ * Stops OFAgent.
+ * Stops OFAgent for the given virtual network.
+ *
+ * @param stream JSON stream
+ * @return 204 NO CONTENT if OpenFlow agent was stopped, 404 NOT FOUND otherwise
+ * @throws IOException if request cannot be parsed
+ */
+ @POST
+ @Path("ofagent-stop")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response stopOFAgent(InputStream stream) throws IOException {
+
+ OFAgentAdminService adminService = get(OFAgentAdminService.class);
+ ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
+ JsonNode networkId = jsonTree.get("networkId");
+
+ if (get(OFAgentService.class).agent(NetworkId.networkId(networkId.asLong())) == null) {
+ return Response.status(NOT_FOUND)
+ .entity(OFAGENT_NOT_FOUND).build();
+ }
+
+ adminService.stopAgent(NetworkId.networkId(networkId.asLong()));
+ return Response.noContent().build();
+ }
+
+
+ /**
+ * Deletes OFAgent.
+ * Removes OFAgent for the given virtual network from repository.
+ *
+ * @param networkId OFAgent networkId
+ * @return 200 OK if OFAgent was removed, 404 NOT FOUND when OF agent does not exist, 400 BAD REQUEST otherwise
+ */
+ @DELETE
+ @Path("ofagent-remove/{networkId}")
+ public Response removeOFAgent(@PathParam("networkId") long networkId) {
+ if (get(OFAgentService.class).agent(NetworkId.networkId(networkId)) == null) {
+ return Response.status(BAD_REQUEST)
+ .entity(OFAGENT_NOT_FOUND).build();
+ }
+
+ OFAgentAdminService adminService = get(OFAgentAdminService.class);
+ OFAgent removed = adminService.removeAgent(NetworkId.networkId(networkId));
+ if (removed != null) {
+ return Response.ok((new OFAgentCodec()).encode(removed, this), MediaType
+ .APPLICATION_JSON_TYPE)
+ .build();
+ } else {
+ return Response.status(NOT_FOUND)
+ .entity(OFAGENT_NOT_FOUND).build();
+ }
+ }
+}
diff --git a/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/OFControllerCodec.java b/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/OFControllerCodec.java
new file mode 100644
index 0000000..e50dccc
--- /dev/null
+++ b/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/OFControllerCodec.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.ofagent.rest;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.ofagent.api.OFController;
+import org.onosproject.ofagent.impl.DefaultOFController;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * OFController JSON codec.
+ */
+public final class OFControllerCodec extends JsonCodec<OFController> {
+
+ private static final String IP = "ip";
+ private static final String PORT = "port";
+
+ private static final String MISSING_MEMBER_MESSAGE = " member is required in OFController";
+
+ @Override
+ public ObjectNode encode(OFController ofController, CodecContext context) {
+ checkNotNull(ofController, "OFController cannot be null");
+
+ return context.mapper().createObjectNode()
+ .put(IP, String.valueOf(ofController.ip()))
+ .put(PORT, String.valueOf(ofController.port()));
+
+ }
+
+ @Override
+ public OFController decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ // parse ip address
+ int id = nullIsIllegal(json.get(IP), IP + MISSING_MEMBER_MESSAGE).asInt();
+
+ // parse port
+ String name = nullIsIllegal(json.get(PORT), PORT + MISSING_MEMBER_MESSAGE).asText();
+
+ return DefaultOFController.of(IpAddress.valueOf(id),
+ TpPort.tpPort(Integer.valueOf(name)));
+ }
+}
diff --git a/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/package-info.java b/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/package-info.java
new file mode 100644
index 0000000..36977f9
--- /dev/null
+++ b/apps/ofagent/src/main/java/org/onosproject/ofagent/rest/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.
+ */
+
+/**
+ * REST API for OFAgent.
+ */
+package org.onosproject.ofagent.rest;
\ No newline at end of file
diff --git a/apps/ofagent/src/main/webapp/WEB-INF/web.xml b/apps/ofagent/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..5c82b0d
--- /dev/null
+++ b/apps/ofagent/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2017-present Open Networking Laboratory
+ ~
+ ~ 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>OFAgent REST API v1.0</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.ofagent.rest.OFAgentWebApplication</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>