sanghoshin
Committed by Gerrit Code Review

SONA : OpenstackSwitching

 - Stateless Neutron data handling
 - Supports Nicira ext.

Change-Id: I31db161bbd06a03e2d8e6ee6abfb033215898ee7
Showing 16 changed files with 416 additions and 81 deletions
......@@ -32,7 +32,8 @@ public final class OpenstackPort {
public enum PortStatus {
UP,
DOWN
DOWN,
ACTIVE
}
private PortStatus status;
......
......@@ -15,6 +15,8 @@
*/
package org.onosproject.openstackswitching;
import org.onosproject.net.Port;
import java.util.Collection;
/**
......@@ -64,6 +66,14 @@ public interface OpenstackSwitchingService {
Collection<OpenstackPort> ports(String networkId);
/**
* Returns port information for the port given.
*
* @param port port reference
* @return port information
*/
OpenstackPort port(Port port);
/**
* Returns port information for the port ID given.
*
* @param portId Port ID
......
......@@ -16,11 +16,13 @@
-->
<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
<repository>mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features</repository>
<feature name="onos-app-openstackswitching" version="${project.version}"
<feature name="${project.artifactId}" version="${project.version}"
description="${project.description}">
<feature>onos-api</feature>
<bundle>mvn:${project.groupId}/onos-app-openstackswitching/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-app-dhcp-api/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-app-dhcp/${project.version}</bundle>
<bundle>mvn:com.sun.jersey/jersey-client/1.19</bundle>
<bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
</feature>
</features>
......
......@@ -90,6 +90,11 @@
<artifactId>onos-app-dhcp-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.19</version>
</dependency>
</dependencies>
<build>
......@@ -110,6 +115,7 @@
javax.ws.rs,
javax.ws.rs.core,
com.sun.jersey.api.core,
com.sun.jersey.api.client,
com.sun.jersey.spi.container.servlet,
com.sun.jersey.server.impl.container.servlet,
com.fasterxml.jackson.databind,
......
......@@ -28,7 +28,8 @@ import org.onosproject.net.packet.PacketService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Handles ARP packet from VMs.
......@@ -38,16 +39,16 @@ public class OpenstackArpHandler {
private static Logger log = LoggerFactory
.getLogger(OpenstackArpHandler.class);
private PacketService packetService;
private Map<String, OpenstackPort> openstackPortMap;
private OpenstackRestHandler restHandler;
/**
* Returns OpenstackArpHandler reference.
*
* @param openstackPortMap
* @param packetService
* @param restHandler rest API handler reference
* @param packetService PacketService reference
*/
public OpenstackArpHandler(Map<String, OpenstackPort> openstackPortMap, PacketService packetService) {
this.openstackPortMap = openstackPortMap;
public OpenstackArpHandler(OpenstackRestHandler restHandler, PacketService packetService) {
this.restHandler = checkNotNull(restHandler);
this.packetService = packetService;
}
......@@ -68,8 +69,9 @@ public class OpenstackArpHandler {
//Searches the Dst MAC Address based on openstackPortMap
MacAddress macAddress = null;
OpenstackPort openstackPort = openstackPortMap.values().stream().filter(e -> e.fixedIps().
containsValue(Ip4Address.valueOf(dstIPAddress))).findAny().orElse(null);
OpenstackPort openstackPort = restHandler.getPorts().stream().
filter(e -> e.fixedIps().containsValue(Ip4Address.valueOf(
dstIPAddress))).findAny().orElse(null);
if (openstackPort != null) {
macAddress = openstackPort.macAddress();
......
/*
* Copyright 2015 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.openstackswitching;
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.Lists;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.onosproject.openstackswitching.web.OpenstackNetworkCodec;
import org.onosproject.openstackswitching.web.OpenstackPortCodec;
import org.onosproject.openstackswitching.web.OpenstackSubnetCodec;
import org.slf4j.Logger;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.MediaType.JSON_UTF_8;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Handles REST Calls to Openstack Neutron.
*
*/
public class OpenstackRestHandler {
private final Logger log = getLogger(getClass());
private String neutronUrl;
private String keystoneUrl;
private String tokenId;
private String userName;
private String pass;
/**
* Creates OpenstackRestHandler instance.
*
* @param cfg OpenstackSwitchingConfig reference
*/
public OpenstackRestHandler(OpenstackSwitchingConfig cfg) {
this.neutronUrl = checkNotNull(cfg.neutronServer());
this.keystoneUrl = checkNotNull(cfg.keystoneServer());
this.userName = checkNotNull(cfg.userName());
this.pass = checkNotNull(cfg.password());
}
/**
* Returns network information stored in Neutron.
*
* @return List of OpenstackNetwork
*/
public Collection<OpenstackNetwork> getNetworks() {
WebResource.Builder builder = getClientBuilder(neutronUrl + "networks");
String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
header("X-Auth-Token", getToken()).get(String.class);
ObjectMapper mapper = new ObjectMapper();
List<OpenstackNetwork> openstackNetworks = Lists.newArrayList();
try {
ObjectNode node = (ObjectNode) mapper.readTree(response);
ArrayNode networkList = (ArrayNode) node.path("networks");
OpenstackNetworkCodec networkCodec = new OpenstackNetworkCodec();
networkList.forEach(n -> openstackNetworks.add(networkCodec.decode((ObjectNode) n, null)));
} catch (IOException e) {
e.printStackTrace();
}
log.debug("networks response:" + response);
openstackNetworks.forEach(n -> log.debug("network ID: {}", n.id()));
return openstackNetworks;
}
/**
* Returns port information stored in Neutron.
*
* @return List of OpenstackPort
*/
public Collection<OpenstackPort> getPorts() {
WebResource.Builder builder = getClientBuilder(neutronUrl + "ports");
String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
header("X-Auth-Token", getToken()).get(String.class);
ObjectMapper mapper = new ObjectMapper();
List<OpenstackPort> openstackPorts = Lists.newArrayList();
try {
ObjectNode node = (ObjectNode) mapper.readTree(response);
ArrayNode portList = (ArrayNode) node.path("ports");
OpenstackPortCodec portCodec = new OpenstackPortCodec();
portList.forEach(p -> openstackPorts.add(portCodec.decode((ObjectNode) p, null)));
} catch (IOException e) {
e.printStackTrace();
}
log.debug("port response:" + response);
openstackPorts.forEach(n -> log.debug("port ID: {}", n.id()));
return openstackPorts;
}
/**
* Returns Subnet information in Neutron.
*
* @return List of OpenstackSubnet
*/
public Collection<OpenstackSubnet> getSubnets() {
WebResource.Builder builder = getClientBuilder(neutronUrl + "subnets");
String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
header("X-Auth-Token", getToken()).get(String.class);
ObjectMapper mapper = new ObjectMapper();
List<OpenstackSubnet> subnets = Lists.newArrayList();
try {
ObjectNode node = (ObjectNode) mapper.readTree(response);
ArrayNode subnetList = (ArrayNode) node.path("subnets");
OpenstackSubnetCodec subnetCodec = new OpenstackSubnetCodec();
subnetList.forEach(s -> subnets.add(subnetCodec.decode((ObjectNode) s, null)));
} catch (IOException e) {
e.printStackTrace();
}
log.debug("subnets response:" + response);
subnets.forEach(s -> log.debug("subnet ID: {}", s.id()));
return subnets;
}
private WebResource.Builder getClientBuilder(String uri) {
Client client = Client.create();
WebResource resource = client.resource(uri);
return resource.accept(JSON_UTF_8.toString())
.type(JSON_UTF_8.toString());
}
private String getToken() {
if (isTokenInvalid()) {
String request = "{\"auth\": {\"tenantName\": \"admin\", " +
"\"passwordCredentials\": {\"username\": \"" +
userName + "\",\"password\": \"" + pass + "\"}}}";
WebResource.Builder builder = getClientBuilder(keystoneUrl + "tokens");
String response = builder.accept(MediaType.APPLICATION_JSON).post(String.class, request);
ObjectMapper mapper = new ObjectMapper();
try {
ObjectNode node = (ObjectNode) mapper.readTree(response);
tokenId = node.path("access").path("token").path("id").asText();
} catch (IOException e) {
e.printStackTrace();
}
log.debug("token response:" + response);
}
return tokenId;
}
private boolean isTokenInvalid() {
//TODO: validation check for the existing token
return true;
}
}
......@@ -24,6 +24,10 @@ import org.onosproject.net.config.basics.BasicElementConfig;
*/
public class OpenstackSwitchingConfig extends Config<ApplicationId> {
public static final String DONOTPUSH = "do_not_push_flows";
public static final String NEUTRON_SERVER = "neutron_server";
public static final String KEYSTONE_SERVER = "keystone_server";
public static final String USER_NAME = "user_name";
public static final String PASSWORD = "password";
/**
* Returns the flag whether the app pushes flows or not.
......@@ -36,6 +40,42 @@ public class OpenstackSwitchingConfig extends Config<ApplicationId> {
}
/**
* Returns the Neutron server IP address.
*
* @return Neutron server IP
*/
public String neutronServer() {
return get(NEUTRON_SERVER, "");
}
/**
* Returns the Keystone server IP address.
*
* @return Keystone server IP
*/
public String keystoneServer() {
return get(KEYSTONE_SERVER, "");
}
/**
* Returns the username for openstack.
*
* @return username for openstack
*/
public String userName() {
return get(USER_NAME, "");
}
/**
* Returns the password for openstack.
*
* @return password for openstack
*/
public String password() {
return get(PASSWORD, "");
}
/**
* Sets the flag whether the app pushes flows or not.
*
* @param flag the flag whether the app pushes flows or not
......@@ -44,4 +84,44 @@ public class OpenstackSwitchingConfig extends Config<ApplicationId> {
public BasicElementConfig doNotPushFlows(boolean flag) {
return (BasicElementConfig) setOrClear(DONOTPUSH, flag);
}
/**
* Sets the neutron server IP address.
*
* @param url neutron server IP address
* @return itself
*/
public BasicElementConfig neutronServer(String url) {
return (BasicElementConfig) setOrClear(NEUTRON_SERVER, url);
}
/**
* Sets the keystone server IP address.
*
* @param url keystone server IP address
* @return itself
*/
public BasicElementConfig keystoneServer(String url) {
return (BasicElementConfig) setOrClear(KEYSTONE_SERVER, url);
}
/**
* Sets the username for openstack.
*
* @param username user name for openstack
* @return itself
*/
public BasicElementConfig userName(String username) {
return (BasicElementConfig) setOrClear(USER_NAME, username);
}
/**
* Sets the password for openstack.
*
* @param password password for openstack
* @return itself
*/
public BasicElementConfig password(String password) {
return (BasicElementConfig) setOrClear(PASSWORD, password);
}
}
......
......@@ -43,6 +43,9 @@ public class OpenstackNetworkCodec extends JsonCodec<OpenstackNetwork> {
public OpenstackNetwork decode(ObjectNode json, CodecContext context) {
JsonNode networkInfo = json.get(NETWORK);
if (networkInfo == null) {
networkInfo = json;
}
String name = networkInfo.path(NAME).asText();
String tenantId = networkInfo.path(TENANT_ID).asText();
......
......@@ -15,48 +15,52 @@
*/
package org.onosproject.openstackswitching.web;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.openstackswitching.OpenstackNetwork;
import org.onosproject.openstackswitching.OpenstackSwitchingService;
import org.onosproject.rest.AbstractWebResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.InputStream;
/**
* Handles REST API call of Neutron ML2 plugin.
*/
@Path("networks")
public class OpenstackNetworkWebResource extends AbstractWebResource {
protected static final Logger log = LoggerFactory
.getLogger(OpenstackNetworkWebResource.class);
private static final OpenstackNetworkCodec NETWORK_CODEC = new OpenstackNetworkCodec();
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createNetwork(InputStream input) {
try {
ObjectMapper mapper = new ObjectMapper();
ObjectNode networkNode = (ObjectNode) mapper.readTree(input);
log.debug("REST API networks is called {}", input.toString());
return Response.status(Response.Status.OK).build();
}
OpenstackNetwork openstackNetwork = NETWORK_CODEC.decode(networkNode, this);
@PUT
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response updateNetwork(InputStream input) {
log.debug("REST API networks is called {}", input.toString());
return Response.status(Response.Status.OK).build();
}
OpenstackSwitchingService switchingService = get(OpenstackSwitchingService.class);
switchingService.createNetwork(openstackNetwork);
return Response.status(Response.Status.OK).build();
} catch (Exception e) {
log.error("Creates VirtualPort failed because of exception {}",
e.toString());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
.build();
}
@DELETE
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response deleteNetwork(InputStream input) {
log.debug("REST API networks is called {}", input.toString());
return Response.status(Response.Status.OK).build();
}
}
......
......@@ -58,6 +58,9 @@ public class OpenstackPortCodec extends JsonCodec<OpenstackPort> {
HashMap<String, Ip4Address> fixedIpMap = new HashMap<>();
JsonNode portInfo = json.get(PORT);
if (portInfo == null) {
portInfo = json;
}
String status = portInfo.path(STATUS).asText();
String name = portInfo.path(NAME).asText();
......
......@@ -33,6 +33,9 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.InputStream;
/**
* Handles Rest API call from Neutron ML2 plugin.
*/
@Path("ports")
public class OpenstackPortWebResource extends AbstractWebResource {
......@@ -50,13 +53,15 @@ public class OpenstackPortWebResource extends AbstractWebResource {
ObjectNode portNode = (ObjectNode) mapper.readTree(input);
OpenstackPort openstackPort = PORT_CODEC.decode(portNode, this);
OpenstackSwitchingService switchingService = get(OpenstackSwitchingService.class);
OpenstackSwitchingService switchingService =
getService(OpenstackSwitchingService.class);
switchingService.createPorts(openstackPort);
log.debug("REST API ports is called with {}", portNode.toString());
return Response.status(Response.Status.OK).build();
} catch (Exception e) {
log.error("Creates VirtualPort failed because of exception {}",
log.error("Creates Port failed because of exception {}",
e.toString());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
.build();
......@@ -64,23 +69,12 @@ public class OpenstackPortWebResource extends AbstractWebResource {
}
@DELETE
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response deletesPorts(InputStream input) {
try {
ObjectMapper mapper = new ObjectMapper();
ObjectNode portNode = (ObjectNode) mapper.readTree(input);
OpenstackSwitchingService switchingService = get(OpenstackSwitchingService.class);
switchingService.deletePorts();
log.info("REST API ports is called with {}", portNode.toString());
return Response.status(Response.Status.OK).build();
} catch (Exception e) {
log.error("Delete VirtualPort failed because of exception {}",
e.toString());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
.build();
}
log.debug("REST API ports is called with {}", input.toString());
return Response.status(Response.Status.OK).build();
}
@PUT
......@@ -88,19 +82,7 @@ public class OpenstackPortWebResource extends AbstractWebResource {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response updatePorts(InputStream input) {
try {
ObjectMapper mapper = new ObjectMapper();
ObjectNode portNode = (ObjectNode) mapper.readTree(input);
OpenstackSwitchingService switchingService = get(OpenstackSwitchingService.class);
switchingService.updatePorts();
log.info("REST API ports is called with {}", portNode.toString());
return Response.status(Response.Status.OK).build();
} catch (Exception e) {
log.error("Update VirtualPort failed because of exception {}",
e.toString());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
.build();
}
log.info("REST API ports is called with {}", input.toString());
return Response.status(Response.Status.OK).build();
}
}
......
......@@ -51,6 +51,9 @@ public class OpenstackSubnetCodec extends JsonCodec<OpenstackSubnet> {
@Override
public OpenstackSubnet decode(ObjectNode json, CodecContext context) {
JsonNode subnetInfo = json.get(SUBNET);
if (subnetInfo == null) {
subnetInfo = json;
}
String name = subnetInfo.path(NAME).asText();
boolean enableDhcp = subnetInfo.path(ENABLE_DHCP).asBoolean();
......
......@@ -15,18 +15,19 @@
*/
package org.onosproject.openstackswitching.web;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.openstackswitching.OpenstackSubnet;
import org.onosproject.openstackswitching.OpenstackSwitchingService;
/**
* Handles Rest API call from Neutron ML2 plugin.
*/
import org.onosproject.rest.AbstractWebResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
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;
......@@ -37,28 +38,32 @@ public class OpenstackSubnetWebResource extends AbstractWebResource {
protected static final Logger log = LoggerFactory
.getLogger(OpenstackSubnetWebResource.class);
private static final OpenstackSubnetCodec SUBNET_CODEC = new OpenstackSubnetCodec();
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createSubnet(InputStream input) {
try {
ObjectMapper mapper = new ObjectMapper();
ObjectNode subnetNode = (ObjectNode) mapper.readTree(input);
return Response.status(Response.Status.OK).build();
}
@PUT
@Path("{subnetUUID}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response updateSubnet(@PathParam("id") String id,
final InputStream input) {
return Response.status(Response.Status.OK).build();
OpenstackSubnet openstackSubnet = SUBNET_CODEC.decode(subnetNode, this);
}
OpenstackSwitchingService switchingService = get(OpenstackSwitchingService.class);
switchingService.createSubnet(openstackSubnet);
log.debug("REST API subnets is called with {}", subnetNode.toString());
return Response.status(Response.Status.OK).build();
} catch (Exception e) {
log.error("Creates VirtualSubnet failed because of exception {}",
e.toString());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
.build();
}
@DELETE
@Path("{subnetUUID}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response deleteSubnet(@PathParam("id") String id,
final InputStream input) {
return Response.status(Response.Status.OK).build();
}
}
......
{
"apps" : {
"org.onosproject.openstackswitching" : {
"openstackswitching" : {
"do_not_push_flows" : "false",
"neutron_server" : "http://127.0.0.1:9696/v2.0/",
"keystone_server" : "http://127.0.0.1:5000/v2.0/",
"user_name" : "admin",
"password" : "nova"
}
},
"org.onosproject.dhcp" : {
"dhcp" : {
"ip": "10.0.0.1",
"mac": "1a:2b:3c:4e:5e:6f",
"subnet": "255.0.0.0",
"broadcast": "10.255.255.255",
"router": "10.0.0.1",
"domain": "10.0.0.1",
"ttl": "63",
"lease": "300",
"renew": "150",
"rebind": "200",
"delay": "3",
"timeout": "150",
"startip": "10.0.0.110",
"endip": "10.0.0.130"
}
},
"org.onosproject.cordvtn" : {
"cordvtn" : {
"ovsdbNodes" : [
{
"host" : "compute-01",
"ip" : "128.199.162.106",
"port" : "6640",
"bridgeId" : "of:0000000000000001"
},
{
"host" : "compute-02",
"ip" : "103.253.145.133",
"port" : "6640",
"bridgeId" : "of:0000000000000002"
},
{
"host" : "network",
"ip" : "128.199.125.11",
"port" : "6640",
"bridgeId" : "of:0000000000000003"
}
]
}
}
}
}