作者简介:王凯,江南大学大四学生,主要学习研究SDN控制器以及OpenStack。
大三有幸跟着学院老师做项目,开始接触OpenStack。说实话一开始的学习对于一个当时连计算机网络都没学过的学生来说是非常痛苦的,好在坚持了下来,从一开始OpenStack的安装到现在SDN的学习,一路“摸爬滚打”,一路积累了我对未来网络的浓厚兴趣。这篇文章是我毕设的一部分,记录一下还是很有必要的。
Ryu应用的实现
本次设计实现的是一个web应用,因此需要用到ryu的WSGI框架以及REST API的开发。关于这两个技术的原理,呈神的博客还是很有参考价值的,在这里我就不做赘述,我主要介绍一下我定义的部分API的实现过程以及功能。
1 2 3 4 5 |
@route('flowmonitor', '/{filename:.*}') def static_handler(self, req, **kwargs): if kwargs['filename']: req.path_info = kwargs['filename'] return self.static_app(req) |
这个API主要实现了加载指定目录下静态网页文件的功能。该功能的实现主要依赖于webob库中的webob.static.DirectoryApp类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
url2 = '/openstack' @route('flowmonitor', url2, methods=['PUT']) def add_flow(self, req, **kwargs): flow_monitor = self.flow_monitor_app try: new_entry = req.json if req.body else {} except ValueError: return Response(status=400) src_instance = new_entry['src'] dst_instance = new_entry['dst'] bandwidth = new_entry['bw'] src_mac = search.parse(src_instance) dst_mac = search.parse(dst_instance) dpid = flow_monitor.datapaths.keys()[0] dp= flow_monitor.datapaths[dpid] ovs_port = search.parser_port(dst_instance) if flow_monitor.qos.has_key(ovs_port): flow_monitor.qos[ovs_port] = flow_monitor.qos[ovs_port] + 1 else: flow_monitor.qos.setdefault(ovs_port,0) if flow_monitor.cmd.has_key(ovs_port): string = 'queues=' + str(flow_monitor.qos[ovs_port]) + '=@q' + str(flow_monitor.qos[ovs_port]) + ',' flow_monitor.cmd[ovs_port] = flow_monitor.cmd[ovs_port].replace('queues=', string) flow_monitor.cmd[ovs_port] = flow_monitor.cmd[ovs_port] + " -- --id=@q" + str(flow_monitor.qos[ovs_port]) + " create queue other-config:min-rate=5000000 other-config:max-rate=" + bandwidth + "000000" else: cmd = "ovs-vsctl -- set port " + ovs_port + " qos=@newqos -- --id=@newqos create qos type=linux-htb other-config:max-rate=1000000000 queues=0=@q0 -- --id=@q0 create queue other-config:min-rate=5000000 other-config:max-rate=" + bandwidth + "000000" flow_monitor.cmd.setdefault(ovs_port,cmd) os.system(flow_monitor.cmd[ovs_port]) flow_monitor.send_flow_mod_openstack(dp, src_mac, dst_mac, flow_monitor.qos[ovs_port]) value={"status":0} body=json.dumps(value) return Response(content_type='application/json', body=body) |
该API是实现带宽限制的关键,请求的数据格式为:{"src":SRC,"dst":DST,"bw":BW},其中SRC代表源主机的id,DST代表目的主机的id,BW代表限制的带宽。因为OpenStack中虚拟机的名字可以重复,但id是唯一的,所以我们必须通过id来唯一标识虚拟机,同时虚拟机的id可以在dashboard中很方便的查看到。
在获取到源主机、目的主机id后,调用search.parse(instance_id)函数,该函数主要完成了根据虚拟机id获取其mac地址的功能,然后针对目的主机id调用search.parser_port(instance_id)函数,返回目的主机连接在br-int上的端口。接着再根据需要限制的带宽在该端口执行添加qos的命令。最后还需要调用flow_monitor.send_flow_mod_openstack函数完成下发流表的功能,实现限速的目的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
url1 = '/login' @route('flowmonitor', url1, methods=['PUT']) def check(self, req, **kwargs): try: new_entry = req.json if req.body else {} except ValueError: return Response(status=400) user= new_entry['user'] passwd= new_entry['passwd'] if (user=='admin') & (passwd == 'wangkai'): body=json.dumps({"status":0}) else: body=json.dumps({"status":1}) return Response(content_type='application/json', body=body) |
这个API主要是实现登录的验证,请求的数据格式为:{"user":user,"passwd":password},user代表用户名,password代表密码。
Ryu连接br-int
本次测试是在OpenStack Mitaka版本之下进行的。一开始在给br-int设置控制器时,一直出问题,在连上的瞬间会断开,很是困惑,不知道是什么原因。后来在实验室师兄的启发下,我追溯了一下br-int的创建过程。
在neutron/plugins/ml2/drivers/openvswitch/agent/ ovs_neutron_agent.py文件中找到了br-int的初始化过程。该文件中的函数setup_integration_br代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def setup_integration_br(self): '''Setup the integration bridge. ''' # Ensure the integration bridge is created. # ovs_lib.OVSBridge.create() will run # ovs-vsctl -- --may-exist add-br BRIDGE_NAME # which does nothing if bridge already exists. self.int_br.create() self.int_br.set_secure_mode() self.int_br.setup_controllers(self.conf) if self.conf.AGENT.drop_flows_on_start: # Delete the patch port between br-int and br-tun if we're deleting # the flows on br-int, so that traffic doesn't get flooded over # while flows are missing. self.int_br.delete_port(self.conf.OVS.int_peer_patch_port) self.int_br.delete_flows() self.int_br.setup_default_table() |
从中可以看出br-int在创建时会调用setup_controllers函数,顾名思义就是设置控制器,于是我继续追溯这个函数的出处,最终在neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ ovs_bridge.py中找到了:
1 2 3 |
def setup_controllers(self, conf): self.set_protocols([ovs_consts.OPENFLOW10]) self.del_controller() |
可以看到该函数调用了del_controller函数,即删除控制器的函数,于是我尝试将这行代码注释后,重启neutron的相关服务,再给br-int设置控制器,发现成功了。至此成功将br-int连上ryu控制器。