Adding REST API - TKTL-SDN/SoftOffload-Master GitHub Wiki
In this page, we will show you how to add a REST API to floodlight (v0.91) with our offloading module as an example. We also hope that this short tutorial could help you understand the basic structure of our SoftOffload REST APIs for future development. In addition, you probably would like to have a look at this official tutorial from floodlight.
To add a new REST API, you have to first create an interface extending IFloodlightService and register it to your module. In SoftOffload, we use this ISoftOffloadService. The service should provide interfaces for your RESTful functionalities, and here we use getClient() to receive client information as our example. The interface will look like this:
package net.floodlightcontroller.mobilesdn;
import net.floodlightcontroller.core.module.IFloodlightService;
public interface ISoftOffloadService extends IFloodlightService {
public Client getClient(String clientMac);
}
Then we need to do a couple things to register this service into our module! Go to our Master module (the main logic module for offloading), and make it implement the ISoftOffloadService. Now the class definition will look like this:
public class Master implements IFloodlightModule, IFloodlightService,
IOFSwitchListener, IOFMessageListener,
IStorageSourceListener,
ISoftOffloadService {
We also need to really implement this interface in the master module:
@Override
public Client getClient(String clientMac) {
return allClientMap.get(clientMac.toLowerCase());
}
allClientMap
is a Hash map we use in the master class to store all the client information. Here, we just use it to return a specific client object stored in the Hash map! In the future step, we will parse this object into a JSON array and return it as the final response.
Next, we register this service into our module, and tell the system that this service is provided in this module:
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(ISoftOffloadService.class);
return l;
}
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Map<Class<? extends IFloodlightService>, IFloodlightService> m =
new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
m.put(ISoftOffloadService.class, this);
return m;
}
To build a real REST API, we also need to add a link from our code to the floodlight REST API service. First, we need to create a RESTful handler for our interface, which returns a snapshot of the function when a REST API request comes. In floodlight, this handler is typically called as resource! Now create a new Class named ClientEntityResource.java:
package net.floodlightcontroller.mobilesdn.web;
import net.floodlightcontroller.mobilesdn.Client;
import net.floodlightcontroller.mobilesdn.ISoftOffloadService;
import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;
public class ClientEntityResource extends ServerResource {
@Get("json")
public Client retrieve() {
ISoftOffloadService sf = (ISoftOffloadService)getContext().getAttributes()
.get(ISoftOffloadService.class.getCanonicalName());
String clientId = (String) getRequestAttributes().get("clientId");
return sf.getClient(clientId);
}
}
retrieve()
will return the real logical code when it is called. Then we could start to set up the routing policy. Create a new class SoftOffloadWebRoutable like this:
package net.floodlightcontroller.mobilesdn.web;
import org.restlet.Context;
import org.restlet.Restlet;
import org.restlet.routing.Router;
import net.floodlightcontroller.restserver.RestletRoutable;
public class SoftOffloadWebRoutable implements RestletRoutable {
@Override
public Restlet getRestlet(Context context) {
Router router = new Router(context);
router.attach("/client/{clientId}/json", ClientEntityResource.class);
return router;
}
@Override
public String basePath() {
return "/wm/softoffload";
}
}
In the routing url, we use a variable {clientId}, and this can be referenced in the ClientEntityResource.class! This class tells the REST API we are registering this API and bind's it's URLs to a specific resource.
Finally, we could start to register our Restlet Routable with the REST API service. First, go back to the Master class, and add a reference to the system REST API service. In the Master class, you should notice that we have an IRestApiService attribute like this:
protected IRestApiService restApi;
Before using it, we have to require IRestApiService as a dependency of our module (like the IFloodlightProviderService). The init() and getModuleDependencies() methods will now look like this:
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
// this is what you need to add
l.add(IRestApiService.class);
return l;
}
@Override
public void init(FloodlightModuleContext context) throws FloodlightModuleException {
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
// init restApi here
restApi = context.getServiceImpl(IRestApiService.class);
IThreadPoolService tp = context.getServiceImpl(IThreadPoolService.class);
executor = tp.getScheduledExecutor();
}
In the startUp function, register our Restlet Routable with the REST API service by adding the following code:
@Override
public void startUp(FloodlightModuleContext context) {
restApi.addRestletRoutable(new SoftOffloadWebRoutable());
}
To return JSON format replies, we use the Jackson library to serialize the object data we return in the Master class. Jackson will look at the object class and try to serialize every field that has a "get" method. In this case we don't want everything to be serialized in Client, so we'll need to write a custom serializer called as ClientJsonSerializer.
package net.floodlightcontroller.mobilesdn.web;
import java.io.IOException;
import net.floodlightcontroller.mobilesdn.APAgent;
import net.floodlightcontroller.mobilesdn.Client;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class ClientJsonSerializer extends JsonSerializer<Client> {
@Override
public void serialize(Client clt, JsonGenerator jGen,
SerializerProvider serializer) throws IOException,
JsonProcessingException {
jGen.writeStartObject();
jGen.writeStringField("mac", clt.getMacAddress().toString());
jGen.writeStringField("ip", clt.getIpAddress().getHostAddress());
jGen.writeStringField("downrate", Double.toString(clt.getDownRate()));
jGen.writeObjectFieldStart("agent");
jGen.writeStringField("ssid", clt.getAgent().getSSID());
jGen.writeStringField("bssid", clt.getAgent().getBSSID());
jGen.writeStringField("ip", clt.getAgent().getIpAddress().getHostAddress());
jGen.writeEndObject();
jGen.writeEndObject();
}
/**
* Tells that we are the serializer for Client
*/
@Override
public Class<Client> handledType() {
return Client.class;
}
}
Now we have to tell Jackson to use the serializer. We do this by annotating our class which will tell Jackson to use our serializer. Open up Client.class and annotate the class as of so.
import net.floodlightcontroller.mobilesdn.web.ClientJsonSerializer;
@JsonSerialize(using=ClientJsonSerializer.class)
public class Client implements Comparable<Object> {