
SimpleSwitchController
SimpleSwitchRest13
코드
# Copyright (C) 2016 Nippon T elegraph and T elephone Corporation.
#
# 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.
import json
from ryu.app import simple_switch_13
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.app.wsgi import route
from ryu.app.wsgi import WSGIApplication
from ryu.lib import dpid as dpid_lib
simple_switch_instance_name = 'simple_switch_api_app'
url = '/simpleswitch/mactable/{dpid}'
class SimpleSwitchRest13(simple_switch_13.SimpleSwitch13):
_CONTEXTS = {'wsgi': WSGIApplication}
def __init__(self, *args, **kwargs):
super(SimpleSwitchRest13, self).__init__(*args, **kwargs)
self.switches = {}
wsgi = kwargs['wsgi']
wsgi.register(SimpleSwitchController, {simple_switch_instance_name: self})
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
super(SimpleSwitchRest13, self).switch_features_handler(ev)
datapath = ev.msg.datapath
self.switches[datapath.id] = datapath
self.mac_to_port.setdefault(datapath.id, {})
def set_mac_to_port(self, dpid, entry):
mac_table = self.mac_to_port.setdefault(dpid, {})
datapath = self.switches.get(dpid)
entry_port = entry['port']
entry_mac = entry['mac']
if datapath is not None:
parser = datapath.ofproto_parser
if entry_port not in mac_table.values():
for mac, port in mac_table.items():
# from known device to new device
actions = [parser.OFPActionOutput(entry_port)]
match = parser.OFPMatch(in_port=port, eth_dst=entry_mac)
self.add_flow(datapath, 1, match, actions)
# from new device to known device
actions = [parser.OFPActionOutput(port)]
match = parser.OFPMatch(in_port=entry_port, eth_dst=mac)
self.add_flow(datapath, 1, match, actions)
mac_table.update({entry_mac: entry_port})
return mac_table
class SimpleSwitchController(ControllerBase):
def __init__(self, req, link, data, **config):
super(SimpleSwitchController, self).__init__(req, link, data, **config)
self.simple_switch_app = data[simple_switch_instance_name]
@route('simpleswitch', url, methods=['GET'], requirements={'dpid': dpid_lib.DPID_PATTERN})
def list_mac_table(self, req, **kwargs):
simple_switch = self.simple_switch_app
dpid = kwargs['dpid']
if dpid not in simple_switch.mac_to_port:
return Response(status=404)
mac_table = simple_switch.mac_to_port.get(dpid, {})
body = json.dumps(mac_table)
return Response(content_type='application/json', text=body)
@route('simpleswitch', url, methods=['PUT'], requirements={'dpid': dpid_lib.DPID_PATTERN})
def put_mac_table(self, req, **kwargs):
simple_switch = self.simple_switch_app
dpid = kwargs['dpid']
try:
new_entry = req.json if req.body else {}
except ValueError:
raise Response(status=400)
if dpid not in simple_switch.mac_to_port:
return Response(status=404)
try:
mac_table = simple_switch.set_mac_to_port(dpid, new_entry)
body = json.dumps(mac_table)
return Response(content_type='application/json', text=body)
except Exception as e:
return Response(status=500)
Ryu 컨트롤러에서 REST API를 통해 스위치의 MAC 주소 테이블을 관리하는 기능을 구현하는 클래스입니다.
클래스 변수 _CONTEXT :

생성자

switch_features_handler

set_mac_to_port


REST API 요청을 받아 MAC 주소 테이블을 조회하거나 등록하는 컨트롤러 클래스입니다.
생성자: SimpleSwitchRest13 클래스의 인스턴스를 가져와서 MAC 주소 테이블을 관리할 수 있도록 합니다.
REST API URL과 해당 프로세스를 구현하는 부분.
데코레이터로 지정하는 내용
두 번째 인수로 지정된 URL에서 REST API가 불려 그 때의 HTTP 방식이 GET인 경우 list_mac_table 메서드를 호출
이 메서드는, {dpid} 부분에서 지정된 데이터 경로 ID에 해당하는 MAC 주소 테이블을 검색하고 JSON으로 변환되어 호출자에게 반환함.
또한, Ryu에 연결되지 않은, 정보가 없는 스위치의 데이터 경로 ID를 지정하면 응답 코드 404를 반환
다음은 MAC 주소 테이블을 등록하는 REST API

먼저 미니넷을 실행함: sudo mn --topo single,3 --mac --switch ovsk --controller remote -x
다른 터미널에서 아래 명령 입력
cd ~/ryu/ryu/app
sudo ovs-vsctl set Bridge s1 protocols=OpenFlow13: mininet에서 s1 ( --switch ovsk)을 실행하였으며 s1을 Bridge로 set 완료
미니넷을 실행하지 않으면 s1이 없다고 함
ryu-manager --verbose ./simple_switch_rest_13.py 명령어 실행
(4264) wsgi starting up on http://0.0.0.0:8080/줄이 있는데, 이는 Web 서버가 포트 번호 8080으로 시작되었음을 나타냄.
다음 mininet shell에서 h1에서 h2로 ping을 실행.
이때 Ryu의 Packet-In은 3번 발생
여기서, 스위칭 허브의 MAC 테이블을 검색하는 REST API를 실행
이번에는 REST API를 호출하기 위해 curl 명령을 사용
세 호스트의 mac 주소 확보
sudo apt install curl
curl -X GET http://127.0.0.1:8080/simpleswitch/mactable/0000000000000001
h1과 h2 두 호스트가 MAC 주소 테이블에서 학습된 것을 알 수 있다.
먼저 스위칭 허브와 Mininet을 중지합니다.

그 다음 다시 Mininet를 시작하고 OpenFlow 버전을 OpenFlow13로 설정 후 스위칭 허브를 시작
이후, MAC 주소 테이블 업데이트를 위한 REST API를 각 호스트마다 호출합니다.
REST API를 호출할 때 데이터 형식은 { mac'':MAC 주소'', port'': 연결
포트 번호}가 되도록 합니다. (포트 8080으로 접속, 8000 아님)
curl -X PUT -d '{"mac" : "00:00:00:00:00:01", "port" : 1}'http://127.0.0.1:8080/simpleswitch/mactable/0000000000000001
controller가 스위치 mac 주소 업데이트를 수락함
h2에 대한 mac 주소를 업데이트함
h3에 대한 mac 주소를 업데이트함
브라우저(파이어폭스)에서 127.0.0.1:8080/index.html 을 이용해서 컨트롤러가 웹서버 REST API로 대응하는지 확인
컨트롤러가 웹서버 REST API로 대응함
