一、案例分析
由于Packet-in消息是Floodlight诸多模块的处理重点,因此统计Packet-in消息在某一段时间的处理量可以在一定程度上反应系统处理性能,所以此案例开发Packet-in消息统计及实现其REST查询服务。案例的基本业务逻辑如下图所示。
二、实例开发
Floodlight中开发新模块首先需要继承IFloodlightMoudle接口,并实现相应的业务逻辑,如果需要将该模块资源以REST API形式发布出去,还需要定义基于该模块的资源包装类,并在REST服务中注册该资源。此外还需要在floodlightdefault.properties和net.floodlightcontroller.core.module.IFloodlightModule文件中增加该模块名配置。
1.服务接口IPacketInHistoryService
新建服务接口IPacketInHistoryService用于查询统计结果
public interface IPacketInHistoryService extends IFloodlightService {
/**
* 该方法用于查询统计结果
* @return
*/
public Long getPacket_InCount();
}
2.新建类PacketInHistory
public class PacketInHistory implements IFloodlightModule, IOFMessageListener, IPacketInHistoryService {
// protected static Logger log= (Logger) LoggerFactory.getLogger(PacketInHistory.class);
protected IFloodlightProviderService floodlightProviderService;
protected IRestApiService restApiService;
private AtomicLong PACKET_IN_COUNT=new AtomicLong(); //对长整形进行原子操作。了解JVM指令的,都知道如果赋值一个long变量,编译后,会产生多个JVM语言指令,如果处于多线程情况下对于这个变量进行加减操作会导致数据不一致。为避免此问题,Java 引入了原子变量 AtomicLong
/**
* 接收在statup()函数中添加监听器所监听的消息
* @param sw the OpenFlow switch that sent this message
* @param msg the message
* @param cntx a Floodlight message context object you can use to pass
* information between listeners
* @return
*/
@Override
public IListener.Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
long count=PACKET_IN_COUNT.incrementAndGet(); //Packet-in消息先加1再获取当前值
Log.info("The total count of Packet-In Messages are : "+count);
return Command.CONTINUE;
}
@Override
public String getName() {
//获取模块名
//return "PacketInHistory";
return PacketInHistory.class.getSimpleName();
}
@Override
public boolean isCallbackOrderingPrereq(OFType type, String name) {
return false;
}
@Override
public boolean isCallbackOrderingPostreq(OFType type, String name) {
return false;
}
/**
* 说明模块的Services
* @return
*/
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Collection< Class< ? extends IFloodlightService>> list=new ArrayList<Class< ? extends IFloodlightService>>();
list.add(IPacketInHistoryService.class);
return list;
// return null;
}
/**
* 说明模块Services的实现对象
* @return
*/
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Map< Class< ? extends IFloodlightService>, IFloodlightService> m=new HashMap<>();
m.put(IPacketInHistoryService.class,this);
return m;
// return null;
}
/**
* 说明模块依赖关系
* @return
*/
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Collection< Class< ? extends IFloodlightService>> list=new ArrayList<Class< ? extends IFloodlightService>>();
list.add(IFloodlightProviderService.class);
list.add(IRestApiService.class);
return list;
}
@Override
public void init(FloodlightModuleContext context) throws FloodlightModuleException {
floodlightProviderService=context.getServiceImpl(IFloodlightProviderService.class); //获取FloodlightProvider(事件管理模块)的实现对象
restApiService=context.getServiceImpl(IRestApiService.class);
}
@Override
public void startUp(FloodlightModuleContext context) throws FloodlightModuleException {
floodlightProviderService.addOFMessageListener(OFType.PACKET_IN,this);
//在类PacketInHistoryWebRoutable中资源绑定相应路径后需要在REST服务中发布,因此此处注册PacketInHistoryWebRoutable(),表明有新资源加入REST服务
restApiService.addRestletRoutable(new PacketInHistoryWebRoutable());
}
@Override
public Long getPacket_InCount() {
return PACKET_IN_COUNT.get(); //获取Packet-in的值
}
}
3.新建类PacketInHistoryResource
模块定义后,需要定义REST服务资源类,新建类PacketInHistoryResource,Superclass设置为ServerResource(ServerResource是Java轻量级REST框架Restlet的抽象类),并实现业务逻辑。主要功能是将Packet-in统计接口包装为REST资源便于后续资源绑定。
/**
* 模块定义后,需要定义REST服务资源类。主要功能是将Packet-in统计接口包装为REST资源便于后续资源绑定。
*/
public class PacketInHistoryResource extends ServerResource {
@Get("json")
public HashMap<String,String> retrieve(){
IPacketInHistoryService pihs=(IPacketInHistoryService)getContext(). //获得Resource类上下文
getAttributes() //
.get( //
IPacketInHistoryService.class.getCanonicalName()); //Java反射获取类名的一种getCanonicalName(),可以获取到内部类的全路径
long count=pihs.getPacket_InCount(); //获取到当前Packet-in消息数量
HashMap<String,String>resp=new HashMap<String, String>(); //存储
resp.put(" Total :",Long.toString(count));
return resp;
}
}
4.新建PacketInHistoryWebRoutable类
/**
* 绑定PacketInHistoryResource资源与访问路径
*/
public class PacketInHistoryWebRoutable implements RestletRoutable {
@Override
public Restlet getRestlet(Context context) {
Router router=new Router(context);
router.attach("/packetinhistroy/json",PacketInHistoryResource.class);
return router;
}
@Override
public String basePath() {
return "/wm/statics";
}
}
三、实验结果
1.Mininet开启,创建拓扑
测试使用的交换机和主机用Mininet模拟,如下命令创建10个交换机和主机
sudo mn --mac --controller=remote,ip=192.168.0.103,port=6633 --switch ovsk --topo linear,10
拓扑图如下。
开启Floodlight控制器(运行Main.class)。在交换机和控制器建立连接后,在Mininet中执行主机ping操作,如h1 ping h2等有如下结果显示。接收Packet-in消息的数量随时间逐步增加。
2.客户端调用REST API
在/< floodlight-path>/example路径下,修改cli.py文件,增加Packet-in消息统计查询命令。修改内容如下
def lookupPath(cmd):
... ...
... ...
elif args.cmd=='packetIn_count':
path='/wm/statics/packetinhistroy/json'
然后在/< floodlight-path>/example路径下,执行命令cli.py packetIn_count有如下结果显示。该命令的使用原理是cli.py作为客户端调用REST API,REST API再调用模块内部服务接口获取统计信息,然后以JSON格式返回统计结果。