2. Ryu & REST API
• Ryu, 웹서버기능 제공
: WSGI 같은 웹서버 기능, REST API 연동 가능
• 목표
1. MAC 주소 테이블 획득 API
: 스위칭 허브가 갖고 있는 MAC주소 테이블의 내용을 반환
: MAC 주소와 포트 번호의 Pair를 JSON 형식으로 반환
2. MAC 주소 테이블 등록 API
: MAC 주소와 포트 번호의 쌍을 MAC주소 테이블에 등록하고 스위치 플로우 항목에 추가
3. Simple_switch_rest_13.py
• Download Sample Code
# wget https://github.com/osrg/ryu-book/blob/master/en/source/sources/simple_switch_rest_13.py
• Simple_swtich_rest_13.py의 클래스
1, SimpleSwitchController : HTTP 요청을 받는 URL과 해당 메소드를 정의하는 컨트롤러 클래스
2. SimpleSwitchRest13 : 스위칭허브를 확장하고 MAC 주소 테이블을 업데이트하는 클래스
스위치에 플로우 항목을 추가 할 것이므로 FeatureReply메서드를 오버라이드하고 datapath 개체를 가짐
4. Class, SimpleSwitchRest13
• View the Code
HTTP 요청을 받는 URL과 해당 메소드를 정의하는 컨트롤러 클래스
class SimpeSwitchRest13(simple_switch_13.SimpleSwitch13):
_CONTEXTS = { 'wsgi': WSGIApplication } # 클래스변수 _CONTEXT에서 Ryu의 WSGI와 호환되는 Web서버 클래스 지정
# wsgi라는 키에서 WSGI의 웹서버 인스턴스를 얻음
...
def __init__(self, *args, **kwargs):
super(SimpleSwitchRest13, self).__init__(*args, **kwargs)
self.switches = {}
wsgi = kwargs['wsgi'] # WSGIAplication의 인스턴스 얻기
wsgi.register(SimpleSwitchController, {simple_switch_instance_name : self}) # register메서드로 등록
... # register 메서드 실행 시, 컨트롤러의 생성자에서 SimpleSwitchRest13클래스의 인스턴스에 액세스 할 수 있도록
# simple_switch_api_app이라는 키 이름으로 dictionary 개체 전달
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev): # 부모 클래스의 switch_features_handler를 오버라이딩
super(SimpleSwitchRest13, self).switch_features_handler(ev)
datapath = ev.msg.datapath # SwitchFeatures 이벤트가 발생한 시간에 이벤트 객체 ev의 datapath개체를 가져옴
self.switches[datapath.id] = datapath # 해당 객체를 switches에 저장
self.mac_to_port.setdefault(datapath.id, {}) # MAC주소 테이블에 초기값으로 빈 dictionary를 설정
...
5. Class, SimpleSwitchRest13
• View the Code
def set_mac_to_port(self, dpid, entry): # 지정된 스위치에 MAC 주소와 포트를 등록하는 메소드, REST API가 PUT방식으로 호출될 때 실행
mac_table = self.mac_to_port.setdefault(dpid, {}) # MAC 주소 테이블 self.mac_to_port의 정보를 참조
datapath = self.switches.get(dpid)
entry_port = entry['port'] # mac, port값이 pair로
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) # 플로우 항목 등록, 부모클래스의 add_flow()
# 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) # 플로우 항목 등록, 부모클래스의 add_flow()
mac_table.update({entry_mac : entry_port}) # entry에서 전달된 정보를 MAC주소 테이블에 저장
return mac_table
…
1 In_port=1, dst_mac = 00:00:00:00:00:02 Output=2
2 In_port=2, dst_mac = 00:00:00:00:00:01 Output=1
Host
A
Host
B
00:00:00:00:00:01, 1 00:00:00:00:00:02, 2
* 등록해야하는 플로우 항목
6. Class, SimpleSwitchController
• View the Code
REST API에 대한 HTTP요청을 수락하는 컨트롤러 클래스
class SimpleSwitchController(ControllerBase):
def __init__(self, req, link, data, **config):
super(SimpleSwitchController, self).__init__(req, link, data, **config) # SimpleSwitchRest13 클래스의 인스턴스를 가져옴
self.simpl_switch_spp = data[simple_switch_instance_name]
...
# REST API URL과 해당 프로세스 구현부
@route('simpleswitch', url, methods=['GET'], requirements={'dpid': dpid_lib.DPID_PATTERN}) # route 데코레이터(설명 뒷장)
def list_mac_table(self, req, **kwargs): # REST API방식이 GET 방식이면 list_mac_table 메서드 호출
simple_switch = self.simpl_switch_spp
dpid = dpid_lib.str_to_dpid(kwargs['dpid']) # {dpid}부분에서 지정된 데이터 경로 ID에 해당하는 MAC주소 테이블 검색
if dpid not in simple_switch.mac_to_port: # 정보가 없는 스위치의 데이터 경로 ID를 지정하면 404에러코드 리턴
return Response(status=404)
mac_table = simple_switch.mac_to_port.get(dpid, {})
body = json.dumps(mac_table) # 검색된 MAC 주소 테이블의 항목이 Json형태로 변환되어 반환
return Response(content_type='application/json', body=body)
…
7. Class, SimpleSwitchController
• View the Code
# MAC주소 테이블을 등록하는 REST API
@route('simpleswitch', url, methods=['PUT'], requirements={'dpid': dpid_lib.DPID_PATTERN}) # REST API가 PUT인 경우
def put_mac_table(self, req, **kwargs): # put_mac_table메서드 호출
simple_switch = self.simpl_switch_spp
dpid = dpid_lib.str_to_dpid(kwargs['dpid'])
new_entry = eval(req.body)
if dpid not in simple_switch.mac_to_port: # Ryu에 연결하지 않은 다른 스위치의 경로 ID를 지정하면
return Response(status=404) # 404 error
try:
mac_table = simple_switch.set_mac_to_port(dpid, new_entry) # set_mac_to_port 메서드 호출
body = json.dumps(mac_table)
return Response(content_type='application/json', body=body)
except Exception as e:
return Response(status=500) # 실패시 500 error
...
* route 데코레이터
인수 이미
1 이름
2 URL 지정. http://<서버IP>:8080/simpleswitch/mactable/<데이터경로 ID>
3 HTTP 메서드 지정(GET 메서드 지정)
4 지정 위치의 형식 지정. URL(/simpleswitch/mactable/{dpid}의 {dpid}부분이 ryu/lib/dpid.py의 DPID_PATTERN에서
정의된 16진수여야 함
8. Ryubook Practice
• 이번엔,
MAC 주소 테이블을 중심으로 REST API를 추가하는 방법에 대해 설명
스위치에 원하는 플로우 항목을 추가하는 REST API 추가 및 사용
• 호스트에서 Mininet 실행
# mn --topo single,3 --mac --switch ovsk --controller remote –x
• 스위치에서 OpenFlow 프로토콜 설정
# ovs-vsctl set Bridge s1 protocols=OpenFlow13
확인
# ovs-ofctl -O OpenFlow13 dump-flows s1
OFPST_FLOW reply (OF1.3) (xid=0x2):
#
[참고] CURL
curl : http://lesstif.com/pages/viewpage.action?pageId=14745703
12. TEST
• 스위칭 허브의 MAC 테이블 검색하는 REST API 실행
REST API를 호출하는 crul 명령
# curl –X GET http://127.0.0.1:8080/simpleswitch/mactable/00000000000000001
> h1, h2가 MAC 주소 테이블에 학습된 것을 확인
13. TEST
• h1, h2를 MAC주소 테이블에 미리 저장 후 결과 확인
mininet 재실행 및 OpenFlow 1.3 재설정
스위치허브 재시작
14. TEST
• h1, h2를 MAC주소 테이블에 미리 저장 후 결과 확인
MAC주소 테이블 업데이트를 위한 REST API를 호스트마다 호출
형식 : {“mac” : “MAC addr”, “port” : port number}
> h1, h2에 대응하는 플로우 항목이 스위치에 등록
16. TEST
• h1, h2를 MAC주소 테이블에 미리 저장 후 결과 확인
1. 스위치에는 이미 플로우 항목이 존재하므로 Packet-In은 h1에서 h2로 ARP요청이 있을때만 발생.
• 2. 이후의 패킷 교환에서는 발생되지 않음
• 3. h2에서 h3으로라는 새로운 플로우 항목에 대하여 새로운 Packet-In발생
1.
2.
3.