Floodlight开发者文档之PKTinHistory

添加模块服务

简介
  控制器由一个负责监听openflowsocket并派发时间的核心模块,以及一些向核心模块注册用于处理响应事件的二级模块构成。当控制器启动时,可启用debuglog,进而看的这些二级模块的注册过程。

  以下创建一个类利用一个buffer来存储近期收到的of消息,并提供rest API进行查询。

创建类

在Eclipse中添加类:

在floodlight项目中找到”src/main/java”文件
在 “src/main/java”文件下选择”New/Class.”
在packet中输入”net.floodlightcontroller.pktinhistory”
在name中输入”PktInHistory”。
在”Interfaces”中选择choose”Add…”,单击“chooseinterface”增加”IFloodlightListener”和”IFloodlightModule”,然后单击“OK”
最后单击“finish”。
得到程序框架。

设置模块依赖关系

模块需要监听openflow消息,因此需要向FloodlightProviderprotected注册,需要增加依赖关系,创建成员变量如下:

protected IFloodlightProviderService floodlightProvider;

然后将新增模块与模块加载相关联,通过完善getModuleDependencies()告知模块加载器在floodlight启动时自己加载。

@Override

public Collection<Class<? extends IFloodlightService>>getModuleDependencies() {

Collection<Class<?extends IFloodlightService>> l = new ArrayList<Class<?extends IFloodlightService>>();

l.add(IFloodlightProviderService.class);

return l;

}

初始化内部变量

@Override

public void init(FloodlightModuleContext context) throwsFloodlightModuleException {

floodlightProvider= context.getServiceImpl(IFloodlightProviderService.class);

}

处理OpenFlow消息
本部分实现对ofpacket_in消息的处理,利用一个buffer来存储近期收到的of消息,以备查询。
在startup()中注册监听器,告诉provider我们希望处理OF的PacketIn消息。

@Override

public void startUp(FloodlightModuleContext context) {

floodlightProvider.addOFMessageListener(OFType.PACKET_IN,this);

}

为OFMessage监听器提供id信息,需调用getName()。

@Override

public String getName() {

return "PktInHistory";

}

对CallbackOrderingPrereq()和isCallbackOrderingPostreq()的调用,只需让它们返回false,packetin消息处理链的执行顺序并不重要。

作为类内部变量,创建circularbuffer(import相关包),存储packetin消息。

protected  ConcurrentCircularBuffer<SwitchMessagePair> buffer;

在初始化过程中初始化该变量。

@Override

public void init(FloodlightModuleContext context) throwsFloodlightModuleException {

floodlightProvider= context.getServiceImpl(IFloodlightProviderService.class);

buffer= newConcurrentCircularBuffer<SwitchMessagePair>(SwitchMessagePair.class,100);

}

最后实现模块接收到packetin消息时的处理动作。

@Override

public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContextcntx) {

switch(msg.getType()){

case PACKET_IN:

buffer.add(new SwitchMessagePair(sw, msg));

break;

default:

break;

}

return Command.CONTINUE;

}

每次packetin发生,其相应消息都会增加相关的交换机消息。该方法返回Command.CONTINUE 以告知IFloodlightProvider能将packetin发
给下一模块,若返回Command.STOP,则指消息就停在该模块不继续被继续处理。

添加restAPI

在实现了一个完整的模块之后,我们可以实现一个restapi,来获取该模块的相关信息。需要完成两件事情:利用创建的模块导出一个服务,并把该服务绑到RESTAPI模块。
具体说来,注册一个新的Restlet,包括
1.在net.floodlightcontroller.controller.internal.Controller中注册一个restlet。
2.实现一个*WebRoutable类。该类实现了RestletRoutable,并提供了getRestlet()和basePath()函数。
3.实现一个*Resource类,该类扩展了ServerResource(),并实现了@Get或@Put函数。

下面具体来看该如何实现。
创建并绑定接口IPktInHistoryService
首先在pktinhistory包中创建一个从IFloodlightService扩展出来的接口IPktInHistoryService(IPktInHistoryService.java),该服务拥有一个方法getBuffer(),来读取circularbuffer中的信息。

package net.floodlightcontroller.pktinhistory;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.core.types.SwitchMessagePair;
public interface IPktinHistoryService extends IFloodlightService {
public ConcurrentCircularBuffer<SwitchMessagePair> getBuffer();
}

现在回到原先创建的PktInHistory.java。相应类定义修订如下,让它具体实现IpktInHistoryService接口,

public class PktInHistory implements IFloodlightModule,IPktinHistoryService, IOFMessageListener {

并实现服务的getBuffer()方法。

@Override
public ConcurrentCircularBuffer<SwitchMessagePair> getBuffer() {
return buffer;
}

通过修改PktInHistory模块中getModuleServices()和getServiceImpls()方法通知模块系统,我们提供了IPktInHistoryService。

@Override
public Collection<Class<? extends IFloodlightService>>getModuleServices() {
Collection<Class<?extends IFloodlightService>> l = new ArrayList<Class<?extends IFloodlightService>>();
l.add(IPktinHistoryService.class);
return l;
}
@Override
public Map<Class<? extends IFloodlightService>,IFloodlightService> getServiceImpls() {
Map<Class<?extends IFloodlightService>, IFloodlightService> m = newHashMap<Class<? extends IFloodlightService>,IFloodlightService>();
m.put(IPktinHistoryService.class,this);
return m;
}

getServiceImpls()会告诉模块系统,本类(PktInHistory)是提供服务的类。
添加变量引用RESTAPI服务
之后,需要添加RESTAPI服务的引用(需要import相关包)。

protected IRestApiService restApi;

并添加IRestApiService作为依赖,这需要修改init()和getModuleDependencies()。

@Override
public Collection<Class<? extends IFloodlightService>>getModuleDependencies() {
Collection<Class<?extends IFloodlightService>> l = new ArrayList<Class<?extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
l.add(IRestApiService.class);
return l;
}
@Override
public void init(FloodlightModuleContext context) throwsFloodlightModuleException {
floodlightProvider= context.getServiceImpl(IFloodlightProviderService.class);
restApi= context.getServiceImpl(IRestApiService.class);
buffer= new ConcurrentCircularBuffer<SwitchMessagePair>(SwitchMessagePair.class,100);
}

创建RESTAPI相关的类PktInHistoryResource和PktInHistoryWebRoutable
现在创建用在RESTAPI中的类,包括两部分,创建处理url call的类和注册到RESTAPI的类。
首先创建处理RESTAPI请求的类PktInHistoryResource(PktInHistoryResource.java)。当请求到达时,该类将返回circularbuffer中的内容。

packagenet.floodlightcontroller.pktinhistory;

importjava.util.ArrayList;
importjava.util.List;
importnet.floodlightcontroller.core.types.SwitchMessagePair;
importorg.restlet.resource.Get;
importorg.restlet.resource.ServerResource;

public class PktInHistoryResource extends ServerResource {
@Get("json")
public List<SwitchMessagePair> retrieve() {
IPktinHistoryService pihr =(IPktinHistoryService)getContext().getAttributes().get(IPktinHistoryService.class.getCanonicalName());
List<SwitchMessagePair> l = new ArrayList<SwitchMessagePair>();
l.addAll(java.util.Arrays.asList(pihr.getBuffer().snapshot()));
return l;
}
}

现在创建PktInHistoryWebRoutable类(PktInHistoryWebRoutable.java),负责告诉RESTAPI我们注册了API并将它的URL绑定到指定的资源上。

packagenet.floodlightcontroller.pktinhistory;
importorg.restlet.Context;
importorg.restlet.Restlet;
importorg.restlet.routing.Router;
importnet.floodlightcontroller.restserver.RestletRoutable;
public class PktInHistoryWebRoutable implements RestletRoutable {
@Override
public Restlet getRestlet(Context context) {
Routerrouter = new Router(context);
router.attach("/history/json",PktInHistoryResource.class);
return router;
}
@Override
public String basePath() {
return"/wm/pktinhistory";
}
}

并将RestletPktInHistoryWebRoutable注册到RESTAPI服务,这通过修改PktInHistory类中的startUp()方法来完成。

@Override
public void startUp(FloodlightModuleContext context) {
floodlightProvider.addOFMessageListener(OFType.PACKET_IN,this);
restApi.addRestletRoutable(new PktInHistoryWebRoutable());
}

自定义序列化类
数据会被Jackson序列化为REST格式。如果需要指定部分序列化,需要自己实现序列化类OFSwitchImplJSONSerializer(OFSwitchImplJSONSerializer.java,位于net.floodlightcontroller.web.serialzers包中),并添加到net.floodlightcontroller.web.serialzers包。

packagenet.floodlightcontroller.core.web.serializers;

importjava.io.IOException;
importnet.floodlightcontroller.core.internal.OFSwitchImpl;
importorg.codehaus.jackson.JsonGenerator;
importorg.codehaus.jackson.JsonProcessingException;
importorg.codehaus.jackson.map.JsonSerializer;
importorg.codehaus.jackson.map.SerializerProvider;
importorg.openflow.util.HexString;

public class OFSwitchImplJSONSerializer extendsJsonSerializer<OFSwitchImpl> {
/**
*Handles serialization for OFSwitchImpl
*/
@Override
public void serialize(OFSwitchImpl switchImpl, JsonGenerator jGen,
SerializerProviderarg2) throws IOException,
JsonProcessingException{
jGen.writeStartObject();
jGen.writeStringField("dpid",HexString.toHexString(switchImpl.getId()));
jGen.writeEndObject();
}
/**
*Tells SimpleModule that we are the serializer for OFSwitchImpl
*/
@Override
public Class<OFSwitchImpl> handledType() {
return OFSwitchImpl.class;
}
}

现在需要告诉Jackson使用我们的序列器。打开OFSwitchImpl.java(位于net.floodlightcontroller.core.internal包),修改如下(需要import我们创建的OFSwitchImplJSONSerializer包)

@JsonSerialize(using=OFSwitchImplJSONSerializer.class)
publicclass OFSwitchImpl implements IOFSwitch {

至此,新建模块基本完成,还需告诉loader我们的模块存在,添加模块名字到src/main/resources/META-INF/services/net.floodlight.core.module.IfloodlightModule。

net.floodlightcontroller.pktinhistory.PktInHistory

然后告知模块需要被加载。修改模块配置文件src/main/resources/floodlightdefault.properties中的floodlight.modules变量。

floodlight.modules= net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher,\
net.floodlightcontroller.forwarding.Forwarding,\
net.floodlightcontroller.pktinhistory.PktInHistory

启动mininet:

mn--controller=remote --ip=[Your IP Address] --mac --topo=tree,2
***Adding controller
***Creating network
***Adding hosts:
h1h2 h3 h4
***Adding switches:
s5s6 s7
***Adding links:
(h1,s6) (h2, s6) (h3, s7) (h4, s7) (s5, s6) (s5, s7)
***Configuring hosts
h1h2 h3 h4
***Starting controller
***Starting 3 switches
s5s6 s7
***Starting CLI:

启动后,运行:

mininet>pingall
***Ping: testing ping reachability
h1-> h2 h3 h4
h2-> h1 h3 h4
h3-> h1 h2 h4
h4-> h1 h2 h3
***Results: 0% dropped (0/12 lost)

利用REST URL拿到结果:

$curl -s http://localhost:8080/wm/pktinhistory/history/json |python -mjson.tool

[

{

"message":{

"bufferId":256,

"inPort":2,

"length":96,

"lengthU":96,

"packetData":"MzP/Uk+PLoqIUk+Pht1gAAAAABg6/wAAAAAAAAAAAAAAAAAAAAD/AgAAAAAAAAAAAAH/Uk+PhwAo2gAAAAD+gAAAAAAAACyKiP/+Uk+P",

"reason":"NO_MATCH",

"totalLength":78,

"type":"PACKET_IN",

"version":1,

"xid": 0

},

"switch":{

"dpid":"00:00:00:00:00:00:00:06"

}

},

{

"message":{

"bufferId":260,

"inPort":1,

"length":96,

"lengthU":96,

"packetData":"MzP/Uk+PLoqIUk+Pht1gAAAAABg6/wAAAAAAAAAAAAAAAAAAAAD/AgAAAAAAAAAAAAH/Uk+PhwAo2gAAAAD+gAAAAAAAACyKiP/+Uk+P",

"reason":"NO_MATCH",

"totalLength":78,

"type":"PACKET_IN",

"version":1,

"xid": 0

},

"switch":{

"dpid":"00:00:00:00:00:00:00:05"

}

},

etc....etc....

附录:完整代码

package net.floodlightcontroller.pktinhistory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFType;


import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.core.types.SwitchMessagePair;
import net.floodlightcontroller.core.IFloodlightProviderService;

import net.floodlightcontroller.restserver.IRestApiService;

import java.util.List; 
import org.restlet.resource.Get; 
import org.restlet.resource.ServerResource; 
import org.restlet.Context; 
import org.restlet.Restlet; 
import org.restlet.routing.Router; 
import net.floodlightcontroller.restserver.RestletRoutable; 


public class PktInHistory implements IFloodlightModule, IOFMessageListener, IPktinHistoryService {

     protected IFloodlightProviderService floodlightProvider;
     protected ConcurrentCircularBuffer<SwitchMessagePair> buffer;

     public ConcurrentCircularBuffer<SwitchMessagePair> getBuffer() { 
         return buffer; 
         } 
     protected IRestApiService restApi; 

     @Override
    public String getName() {
          return "PktInHistory";
    }

    public class PktInHistoryResource extends ServerResource { 
        @Get("json") 
        public List<SwitchMessagePair> retrieve() { 
        IPktinHistoryService pihr = (IPktinHistoryService)getContext().getAttributes().get(IPktinHistoryService.class.getCanonicalName()); 
        List<SwitchMessagePair> l = new ArrayList<SwitchMessagePair>(); 
        l.addAll(java.util.Arrays.asList(pihr.getBuffer().snapshot())); 
        return l; 
        } 
        } 

    public class PktInHistoryWebRoutable implements RestletRoutable { 
        @Override 
        public Restlet getRestlet(Context context) { 
        Router router = new Router(context); 
        router.attach("/history/json", PktInHistoryResource.class);
        return router; 
        } 
        @Override 
        public String basePath() { 
        return "/wm/pktinhistory"; 
        } 
        } 

    @Override
    public boolean isCallbackOrderingPrereq(OFType type, String name) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isCallbackOrderingPostreq(OFType type, String name) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public net.floodlightcontroller.core.IListener.Command receive(
            IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
         switch(msg.getType()) {
         case PACKET_IN:
             buffer.add(new SwitchMessagePair(sw, msg));
             break;
        default:
            break;
         }
         return Command.CONTINUE;
    }

    @Override
    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
        Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); 
        l.add(IPktinHistoryService.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(IPktinHistoryService.class, this); 
        return m; 
    }

    @Override
    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
        Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
        l.add(IFloodlightProviderService.class);
        l.add(IRestApiService.class); 
        return l;
    }

    @Override
    public void init(FloodlightModuleContext context)
            throws FloodlightModuleException {
         floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
         restApi = context.getServiceImpl(IRestApiService.class); 
         buffer = new ConcurrentCircularBuffer<SwitchMessagePair>(SwitchMessagePair.class, 100);

    }

    @Override
    public void startUp(FloodlightModuleContext context) {
         floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
         restApi.addRestletRoutable(new PktInHistoryWebRoutable()); 
    }

}

ConcurrentCircularBuffer:

package net.floodlightcontroller.pktinhistory;
import java.util.concurrent.atomic.AtomicInteger;
import java.lang.reflect.Array;

public class ConcurrentCircularBuffer <T> {
    private final AtomicInteger cursor = new AtomicInteger();
    private final Object[]      buffer;
    private final Class<T>      type;

    public ConcurrentCircularBuffer (final Class <T> type,
                                         final int bufferSize)
    {
        if (bufferSize < 1) {
            throw new IllegalArgumentException(
                    "Buffer size must be a positive value"
                );
        }

            this.type   = type;
            this.buffer = new Object [ bufferSize ];
        }

        public void add (T sample) {
            buffer[ cursor.getAndIncrement() % buffer.length ] = sample;
        }

   @SuppressWarnings("unchecked")
        public T[] snapshot () {
        Object[] snapshots = new Object [ buffer.length ];

            /* Identify the start-position of the buffer. */
            int before = cursor.get();

        /* Terminate early for an empty buffer. */
        if (before == 0) {
                return (T[]) Array.newInstance(type, 0);
            }

            System.arraycopy(buffer, 0, snapshots, 0, buffer.length);

            int after          = cursor.get();
            int size           = buffer.length - (after - before);
            int snapshotCursor = before - 1;

            /* The entire buffer was replaced during the copy. */
        if (size <= 0) {
                return (T[]) Array.newInstance(type, 0);
        }

            int start = snapshotCursor - (size - 1);
            int end   = snapshotCursor;

            if (snapshotCursor < snapshots.length) {
                size   = snapshotCursor + 1;
            start  = 0;
            }

            /* Copy the sample snapshot to a new array the size of our stable
         * snapshot area.
             */
            T[] result = (T[]) Array.newInstance(type, size);

            int startOfCopy = start % snapshots.length;
            int endOfCopy   = end   % snapshots.length;

            /* If the buffer space wraps the physical end of the array, use two
             * copies to construct the new array.
             */
            if (startOfCopy > endOfCopy) {
                System.arraycopy(snapshots, startOfCopy,
                                 result, 0,
                                 snapshots.length - startOfCopy);
                System.arraycopy(snapshots, 0,
                                 result, (snapshots.length - startOfCopy),
                                 endOfCopy + 1);
            }
            else {
                /* Otherwise it's a single continuous segment, copy the whole thing
                 * into the result.
                 */
                System.arraycopy(snapshots, startOfCopy, result, 0, size);
        }

            return (T[]) result;
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值