diff options
author | YAMADA Hideki <yamada.hideki@po.ntts.co.jp> | 2013-03-28 18:50:27 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2013-03-31 18:13:12 +0900 |
commit | fd7a4e61197cbc6657b64f3658178081b059a762 (patch) | |
tree | e2aae893fc242a428b8658001f48775584e27213 | |
parent | 2a7e193fae8d55066e7acfe89e52903053f30433 (diff) |
topology: Add REST API
1. run topology/switches and app/rest_topology
$ ./bin/ryu-manager --verbose --observe-links ryu/topology/switches.py ryu/app/rest_topology.py
2. You can get topology json
$ curl http://127.0.0.1:8080/v1.0/topology/links
[{"src": {"hw_addr": "06:96:65:51:f8:ff", "name": "s2-eth3", "port_no": "00000003", "dpid": "0000000000000002"}, "dst": {"hw_addr": "4a:ed:13:ea:b7:e0", "name": "s1-eth1", "port_no": "00000001", "dpid": "0000000000000001"}}, {"src": {"hw_addr": "4a:ed:13:ea:b7:e0", "name": "s1-eth1", "port_no": "00000001", "dpid": "0000000000000001"}, "dst": {"hw_addr": "06:96:65:51:f8:ff", "name": "s2-eth3", "port_no": "00000003", "dpid": "0000000000000002"}}, {"src": {"hw_addr": "1e:7b:0b:fc:0a:2f", "name": "s1-eth2", "port_no": "00000002", "dpid": "0000000000000001"}, "dst": {"hw_addr": "4e:62:98:6f:16:57", "name": "s3-eth3", "port_no": "00000003", "dpid": "0000000000000003"}}, {"src": {"hw_addr": "4e:62:98:6f:16:57", "name": "s3-eth3", "port_no": "00000003", "dpid": "0000000000000003"}, "dst": {"hw_addr": "1e:7b:0b:fc:0a:2f", "name": "s1-eth2", "port_no": "00000002", "dpid": "0000000000000001"}}]
$ curl http://127.0.0.1:8080/v1.0/topology/switches
[{"ports": [{"hw_addr": "4a:ed:13:ea:b7:e0", "name": "s1-eth1", "port_no": "00000001", "dpid": "0000000000000001"}, {"hw_addr": "1e:7b:0b:fc:0a:2f", "name": "s1-eth2", "port_no": "00000002", "dpid": "0000000000000001"}], "dpid": "0000000000000001"}, {"ports": [{"hw_addr": "be:3b:8d:77:14:a5", "name": "s2-eth1", "port_no": "00000001", "dpid": "0000000000000002"}, {"hw_addr": "1e:bf:ec:8b:c2:5b", "name": "s2-eth2", "port_no": "00000002", "dpid": "0000000000000002"}, {"hw_addr": "06:96:65:51:f8:ff", "name": "s2-eth3", "port_no": "00000003", "dpid": "0000000000000002"}], "dpid": "0000000000000002"}, {"ports": [{"hw_addr": "52:a2:9a:7a:a8:a1", "name": "s3-eth1", "port_no": "00000001", "dpid": "0000000000000003"}, {"hw_addr": "62:25:ff:a5:a1:1a", "name": "s3-eth2", "port_no": "00000002", "dpid": "0000000000000003"}, {"hw_addr": "4e:62:98:6f:16:57", "name": "s3-eth3", "port_no": "00000003", "dpid": "0000000000000003"}], "dpid": "0000000000000003"}]
Signed-off-by: YAMADA Hideki <yamada.hideki@po.ntts.co.jp>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r-- | ryu/app/rest_topology.py | 99 | ||||
-rw-r--r-- | ryu/topology/switches.py | 24 |
2 files changed, 120 insertions, 3 deletions
diff --git a/ryu/app/rest_topology.py b/ryu/app/rest_topology.py new file mode 100644 index 00000000..34c8f532 --- /dev/null +++ b/ryu/app/rest_topology.py @@ -0,0 +1,99 @@ +# Copyright (C) 2013 Nippon Telegraph and Telephone 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 webob import Response + +from ryu.app.wsgi import ControllerBase, WSGIApplication +from ryu.base import app_manager +from ryu.lib import dpid as dpid_lib +from ryu.lib import port_no as port_no_lib +from ryu.topology.switches import get_switch, get_link + +# REST API for switch configuration +# +# get all the switches +# GET /v1.0/topology/switches +# +# get the switch +# GET /v1.0/topology/switches/<dpid> +# +# get all the links +# GET /v1.0/topology/links +# +# get the links of a switch +# GET /v1.0/topology/links/<dpid> +# +# where +# <dpid>: datapath id in 16 hex + + +class TopologyController(ControllerBase): + def __init__(self, req, link, data, **config): + super(TopologyController, self).__init__(req, link, data, **config) + self.topology_api_app = data['topology_api_app'] + + def list_switches(self, req, **kwargs): + dpid = None + if 'dpid' in kwargs: + dpid = dpid_lib.str_to_dpid(kwargs['dpid']) + switches = get_switch(self.topology_api_app, dpid) + body = json.dumps([switch.to_dict() for switch in switches]) + return Response(content_type='application/json', body=body) + + def list_links(self, req, **kwargs): + dpid = None + if 'dpid' in kwargs: + dpid = dpid_lib.str_to_dpid(kwargs['dpid']) + links = get_link(self.topology_api_app, dpid) + body = json.dumps([link.to_dict() for link in links]) + return Response(content_type='application/json', body=body) + + +class TopologyAPI(app_manager.RyuApp): + _CONTEXTS = { + 'wsgi': WSGIApplication + } + + def __init__(self, *args, **kwargs): + super(TopologyAPI, self).__init__(*args, **kwargs) + wsgi = kwargs['wsgi'] + mapper = wsgi.mapper + + controller = TopologyController + wsgi.registory[controller.__name__] = {'topology_api_app': self} + route_name = 'topology' + + uri = '/v1.0/topology/switches' + mapper.connect(route_name, uri, controller=controller, + action='list_switches', + conditions=dict(method=['GET'])) + + uri = '/v1.0/topology/switches/{dpid}' + requirements = {'dpid': dpid_lib.DPID_PATTERN} + s = mapper.submapper(controller=controller, requirements=requirements) + s.connect(route_name, uri, action='list_switches', + conditions=dict(method=['GET'])) + + uri = '/v1.0/topology/links' + mapper.connect(route_name, uri, controller=controller, + action='list_links', + conditions=dict(method=['GET'])) + + uri = '/v1.0/topology/links/{dpid}' + requirements = {'dpid': dpid_lib.DPID_PATTERN} + s = mapper.submapper(controller=controller, requirements=requirements) + s.connect(route_name, uri, action='list_links', + conditions=dict(method=['GET'])) diff --git a/ryu/topology/switches.py b/ryu/topology/switches.py index 318473fa..d507506b 100644 --- a/ryu/topology/switches.py +++ b/ryu/topology/switches.py @@ -17,6 +17,7 @@ import logging import gevent import struct import time +import json from oslo.config import cfg from ryu.topology import event @@ -25,8 +26,9 @@ from ryu.controller import ofp_event from ryu.controller.handler import set_ev_cls from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER from ryu.exception import RyuException -from ryu.lib.mac import DONTCARE +from ryu.lib.mac import DONTCARE, haddr_to_str from ryu.lib.dpid import dpid_to_str, str_to_dpid +from ryu.lib.port_no import port_no_to_str from ryu.lib.packet import packet, ethernet, lldp from ryu.ofproto.ether import ETH_TYPE_LLDP from ryu.ofproto import ofproto_v1_0 @@ -77,6 +79,12 @@ class Port(object): # return (self._state & self._ofproto.OFPPS_LIVE) > 0 return not self.is_down() + def to_dict(self): + return {'dpid': dpid_to_str(self.dpid), + 'port_no': port_no_to_str(self.port_no), + 'hw_addr': haddr_to_str(self.hw_addr), + 'name': self.name.rstrip('\0')} + # for Switch.del_port() def __eq__(self, other): return self.dpid == other.dpid and self.port_no == other.port_no @@ -109,6 +117,11 @@ class Switch(object): def del_port(self, ofpport): self.ports.remove(Port(ofpport)) + def to_dict(self): + d = {'dpid': dpid_to_str(self.dp.id), + 'ports': [port.to_dict() for port in self.ports]} + return d + def __str__(self): msg = 'Switch<dpid=%s, ' % self.dp.id for port in self.ports: @@ -125,6 +138,11 @@ class Link(object): self.src = src self.dst = dst + def to_dict(self): + d = {'src': self.src.to_dict(), + 'dst': self.dst.to_dict()} + return d + # this type is used for key value of LinkState def __eq__(self, other): return self.src == other.src and self.dst == other.dst @@ -421,8 +439,8 @@ class Switches(app_manager.RyuApp): LINK_TIMEOUT = TIMEOUT_CHECK_PERIOD * 2 LINK_LLDP_DROP = 5 - def __init__(self): - super(Switches, self).__init__() + def __init__(self, *args, **kwargs): + super(Switches, self).__init__(*args, **kwargs) self.name = 'switches' self.dps = {} # datapath_id => Datapath class |