Remove os_ken/app and os_ken/services/protocols/bgp/api/jsonrpc modules

The os_ken/services/protocols/bgp/api/jsonrpc.py module was using
os_ken/app and os_ken app is the only one which was using tinyrpc lib.
Modules under os_ken/app except os_ken/app/ofctl are not used in neutron
and neutron-dynamic-routing so lets drop them from the code.

With that we can also drop tinyrpc from the requirements file at all.

Task: #41912
Story: #2008648
Change-Id: Ic35d1f7ee4112bc5cf16fee3d828534ded26ce7f
This commit is contained in:
Slawek Kaplonski 2021-02-24 16:29:07 +01:00 committed by Akihiro Motoki
parent 28b0341e48
commit cd4926b235
61 changed files with 0 additions and 14697 deletions

View File

@ -10,6 +10,3 @@ Others provide some functionalities to other OS-Ken applications.
:maxdepth: 1
app/ofctl.rst
app/ofctl_rest.rst
app/rest_vtep.rst
app/bgp_application.rst

View File

@ -1,6 +0,0 @@
*****************************************
os_ken.services.protocols.bgp.application
*****************************************
.. automodule:: os_ken.services.protocols.bgp.application
:members:

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +0,0 @@
********************
os_ken.app.rest_vtep
********************
.. automodule:: os_ken.app.rest_vtep
REST API
========
.. autoclass:: os_ken.app.rest_vtep.RestVtepController
:members:
:member-order: bysource

View File

@ -86,14 +86,6 @@ os_ken.ofproto.ofproto_v1_5_parser
OS-Ken applications
===================
os_ken.app.cbench
-----------------
.. automodule:: os_ken.app.cbench
os_ken.app.simple_switch
------------------------
.. automodule:: os_ken.app.simple_switch
os_ken.topology
---------------
.. automodule:: os_ken.topology

View File

@ -6,5 +6,4 @@ Configuration
:maxdepth: 2
tls.rst
gui.rst

View File

@ -1,33 +0,0 @@
***************
Topology Viewer
***************
os_ken.app.gui_topology.gui_topology provides topology visualization.
This depends on following os_ken applications.
======================== =================================================
os_ken.app.rest_topology Get node and link data.
os_ken.app.ws_topology Being notified change of link up/down.
os_ken.app.ofctl_rest Get flows of datapaths.
======================== =================================================
Usage
=====
Run mininet (or join your real environment)::
$ sudo mn --controller remote --topo tree,depth=3
Run GUI application::
$ PYTHONPATH=. ./bin/os_ken run --observe-links os_ken/app/gui_topology/gui_topology.py
Access http://<ip address of os_ken host>:8080 with your web browser.
Screenshot
==========
.. image:: gui.png
:width: 640 px

View File

@ -42,27 +42,12 @@ It can be configured by passing configuration file like::
osken-manager [generic/application specific options...]
At the moment applications including the following ones are available
(And more to come as OS-Ken evolves.)
* os_ken.app.simple_isolation.SimpleIsolation
* os_ken.app.rest.RestAPI
* os_ken.app.simple_bridge.SimpleSwitch
The generic available is as follows::
--app-lists: application module name to run;
repeat this option to specify a list of values
--help: show help
The options for REST server::
--wsapi-host: webapp listen host
(default: '')
--wsapi-port: webapp listen port
(default: '8080')
(an integer)
The options for openflow controller::
--ofp-listen-host: openflow listen host
@ -96,38 +81,3 @@ The option for oslo.config.cfg::
individual options are over-ridden. The set is parsed after the file(s),
if any, specified via --config-file, hence over-ridden options in the
directory take precedence.
Invoking Example
================
The example is as follows::
% osken-manager --wsapi-port 8081 --verbose --app-lists os_ken.app.simple_isolation,os_ken.app.rest
loading app os_ken.app.simple_isolation
loading app os_ken.app.rest
loading app os_ken.controller.ofp_handler
creating context dpset
creating context wsgi
creating context network
instantiating app os_ken.app.simple_isolation
instantiating app os_ken.app.rest
instantiating app os_ken.controller.ofp_handler
BRICK dpset
CONSUMES EventOFPStateChange
CONSUMES EventOFPPortStatus
CONSUMES EventOFPSwitchFeatures
BRICK ofp_event
PROVIDES EventOFPStateChange TO ['dpset']
PROVIDES EventOFPPortStatus TO ['dpset', 'SimpleIsolation']
PROVIDES EventOFPPacketIn TO ['SimpleIsolation']
PROVIDES EventOFPSwitchFeatures TO ['dpset', 'SimpleIsolation']
CONSUMES EventOFPEchoRequest
CONSUMES EventOFPErrorMsg
CONSUMES EventOFPSwitchFeatures
CONSUMES EventOFPHello
BRICK network
BRICK RestAPI
BRICK SimpleIsolation
CONSUMES EventOFPPacketIn
CONSUMES EventOFPPortStatus
CONSUMES EventOFPSwitchFeatures

View File

@ -176,15 +176,3 @@ Starting LINC OpenFlow switch
Then run LINC::
# rel/linc/bin/linc console
Run OS-Ken test_of_config app
=============================
Run test_of_config app::
# osken-manager --verbose os_ken.tests.integrated.test_of_config os_ken.app.rest
If you don't install os_ken and are working in the git repo directly::
# osken-manager --verbose os_ken.tests.integrated.test_of_config os_ken.app.rest

View File

@ -126,7 +126,6 @@ testrepository==0.0.18
testresources==2.0.0
testscenarios==0.4
testtools==2.2.0
tinyrpc==0.6
traceback2==1.4.0
unittest2==1.1.0
vine==1.1.4

View File

@ -1,98 +0,0 @@
# Copyright (C) 2014 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 os
import socket
import time
from os_ken.base import app_manager
from os_ken.lib import hub
from os_ken.lib.hub import StreamServer
from os_ken.lib.packet import bmp
class BMPStation(app_manager.OSKenApp):
def __init__(self):
super(BMPStation, self).__init__()
self.name = 'bmpstation'
self.server_host = os.environ.get('OSKEN_BMP_SERVER_HOST', '0.0.0.0')
self.server_port = int(os.environ.get('OSKEN_BMP_SERVER_PORT', 11019))
output_file = os.environ.get('OSKEN_BMP_OUTPUT_FILE', 'os_ken_bmp.log')
failed_dump = os.environ.get('OSKEN_BMP_FAILED_DUMP',
'os_ken_bmp_failed.dump')
self.output_fd = open(output_file, 'w')
self.failed_dump_fd = open(failed_dump, 'w')
self.failed_pkt_count = 0
def start(self):
super(BMPStation, self).start()
self.logger.debug("listening on %s:%s", self.server_host,
self.server_port)
return hub.spawn(StreamServer((self.server_host, self.server_port),
self.loop).serve_forever)
def loop(self, sock, addr):
self.logger.debug("BMP client connected, ip=%s, port=%s", addr[0],
addr[1])
is_active = True
buf = bytearray()
required_len = bmp.BMPMessage._HDR_LEN
while is_active:
ret = sock.recv(required_len)
if len(ret) == 0:
is_active = False
break
buf += ret
while len(buf) >= required_len:
version, len_, _ = bmp.BMPMessage.parse_header(buf)
if version != bmp.VERSION:
self.logger.error("unsupported bmp version: %d", version)
is_active = False
break
required_len = len_
if len(buf) < required_len:
break
try:
msg, rest = bmp.BMPMessage.parser(buf)
except Exception as e:
pkt = buf[:len_]
self.failed_dump_fd.write(pkt)
self.failed_dump_fd.flush()
buf = buf[len_:]
self.failed_pkt_count += 1
self.logger.error("failed to parse: %s"
" (total fail count: %d)",
e, self.failed_pkt_count)
else:
t = time.strftime("%Y %b %d %H:%M:%S", time.localtime())
self.logger.debug("%s | %s | %s\n", t, addr[0], msg)
self.output_fd.write("%s | %s | %s\n\n" % (t, addr[0],
msg))
self.output_fd.flush()
buf = rest
required_len = bmp.BMPMessage._HDR_LEN
self.logger.debug("BMP client disconnected, ip=%s, port=%s", addr[0],
addr[1])
sock.close()

View File

@ -1,50 +0,0 @@
# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2012 Isaku Yamahata <yamahata at valinux co jp>
#
# 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.
"""
A dumb OpenFlow 1.0 responder for benchmarking the controller framework.
Intended to be used with oflops cbench.
"""
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_0
class Cbench(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(Cbench, self).__init__(*args, **kwargs)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
match = datapath.ofproto_parser.OFPMatch(
ofproto_v1_0.OFPFW_ALL, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0)
mod = datapath.ofproto_parser.OFPFlowMod(
datapath, match=match, cookie=0, command=ofproto.OFPFC_ADD,
idle_timeout=0, hard_timeout=0,
priority=ofproto.OFP_DEFAULT_PRIORITY,
flags=0, actions=None)
datapath.send_msg(mod)

View File

@ -1,18 +0,0 @@
# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2012 Isaku Yamahata <yamahata at private email ne jp>
#
# 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.
OVSDB_ADDR = 'ovsdb_addr' # value <method>:<ip>[:<port>]
OVS_TUNNEL_ADDR = 'ovs_tunnel_addr' # ip address of tunnel

View File

@ -1,101 +0,0 @@
# Copyright (C) 2016 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.
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_3
from os_ken.lib.packet import packet
from os_ken.lib.packet import ethernet
class ExampleSwitch13(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(ExampleSwitch13, self).__init__(*args, **kwargs)
# initialize mac address table.
self.mac_to_port = {}
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# install the table-miss flow entry.
match = parser.OFPMatch()
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
ofproto.OFPCML_NO_BUFFER)]
self.add_flow(datapath, 0, match, actions)
def add_flow(self, datapath, priority, match, actions):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# construct flow_mod message and send it.
inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
actions)]
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
match=match, instructions=inst)
datapath.send_msg(mod)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# get Datapath ID to identify OpenFlow switches.
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
# analyse the received packets using the packet library.
pkt = packet.Packet(msg.data)
eth_pkt = pkt.get_protocol(ethernet.ethernet)
dst = eth_pkt.dst
src = eth_pkt.src
# get the received port number from packet_in message.
in_port = msg.match['in_port']
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = in_port
# if the destination mac address is already learned,
# decide which port to output the packet, otherwise FLOOD.
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
# construct action list.
actions = [parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time.
if out_port != ofproto.OFPP_FLOOD:
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
self.add_flow(datapath, 1, match, actions)
# construct packet_out message and send it.
out = parser.OFPPacketOut(datapath=datapath,
buffer_id=ofproto.OFP_NO_BUFFER,
in_port=in_port, actions=actions,
data=msg.data)
datapath.send_msg(out)

View File

@ -1,68 +0,0 @@
# Copyright (C) 2014 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.
"""
Usage example
1. Join switches (use your favorite method):
$ sudo mn --controller remote --topo tree,depth=3
2. Run this application:
$ PYTHONPATH=. ./bin/os_ken run \
--observe-links os_ken/app/gui_topology/gui_topology.py
3. Access http://<ip address of os_ken host>:8080 with your web browser.
"""
import os
from webob.static import DirectoryApp
from os_ken.app.wsgi import ControllerBase, WSGIApplication, route
from os_ken.base import app_manager
PATH = os.path.dirname(__file__)
# Serving static files
class GUIServerApp(app_manager.OSKenApp):
_CONTEXTS = {
'wsgi': WSGIApplication,
}
def __init__(self, *args, **kwargs):
super(GUIServerApp, self).__init__(*args, **kwargs)
wsgi = kwargs['wsgi']
wsgi.register(GUIServerController)
class GUIServerController(ControllerBase):
def __init__(self, req, link, data, **config):
super(GUIServerController, self).__init__(req, link, data, **config)
path = "%s/html/" % PATH
self.static_app = DirectoryApp(path)
@route('topology', '/{filename:[^/]*}')
def static_handler(self, req, **kwargs):
if kwargs['filename']:
req.path_info = kwargs['filename']
return self.static_app(req)
app_manager.require_app('os_ken.app.rest_topology')
app_manager.require_app('os_ken.app.ws_topology')
app_manager.require_app('os_ken.app.ofctl_rest')

View File

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="./osken.topology.css">
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<h1>OSKen Topology Viewer</h1>
<script src="./osken.topology.js" charset="utf-8"></script>
</body>
</html>

View File

@ -1,30 +0,0 @@
#topology {
border: 1px solid #000000;
}
.node {
}
.node.fixed {
fill: #C0C0C0;
}
.node text {
font-size: 14px;
}
.link {
stroke: #090909;
stroke-opacity: .6;
stroke-width: 2px;
}
.port circle {
stroke: black;
fill: #C5F9F9;
}
.port text {
font-size: 10px;
}

View File

@ -1,281 +0,0 @@
var CONF = {
image: {
width: 50,
height: 40
},
force: {
width: 960,
height: 500,
dist: 200,
charge: -600
}
};
var ws = new WebSocket("ws://" + location.host + "/v1.0/topology/ws");
ws.onmessage = function(event) {
var data = JSON.parse(event.data);
var result = rpc[data.method](data.params);
var ret = {"id": data.id, "jsonrpc": "2.0", "result": result};
this.send(JSON.stringify(ret));
}
function trim_zero(obj) {
return String(obj).replace(/^0+/, "");
}
function dpid_to_int(dpid) {
return Number("0x" + dpid);
}
var elem = {
force: d3.layout.force()
.size([CONF.force.width, CONF.force.height])
.charge(CONF.force.charge)
.linkDistance(CONF.force.dist)
.on("tick", _tick),
svg: d3.select("body").append("svg")
.attr("id", "topology")
.attr("width", CONF.force.width)
.attr("height", CONF.force.height),
console: d3.select("body").append("div")
.attr("id", "console")
.attr("width", CONF.force.width)
};
function _tick() {
elem.link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
elem.node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
elem.port.attr("transform", function(d) {
var p = topo.get_port_point(d);
return "translate(" + p.x + "," + p.y + ")";
});
}
elem.drag = elem.force.drag().on("dragstart", _dragstart);
function _dragstart(d) {
var dpid = dpid_to_int(d.dpid)
d3.json("/stats/flow/" + dpid, function(e, data) {
flows = data[dpid];
console.log(flows);
elem.console.selectAll("ul").remove();
li = elem.console.append("ul")
.selectAll("li");
li.data(flows).enter().append("li")
.text(function (d) { return JSON.stringify(d, null, " "); });
});
d3.select(this).classed("fixed", d.fixed = true);
}
elem.node = elem.svg.selectAll(".node");
elem.link = elem.svg.selectAll(".link");
elem.port = elem.svg.selectAll(".port");
elem.update = function () {
this.force
.nodes(topo.nodes)
.links(topo.links)
.start();
this.link = this.link.data(topo.links);
this.link.exit().remove();
this.link.enter().append("line")
.attr("class", "link");
this.node = this.node.data(topo.nodes);
this.node.exit().remove();
var nodeEnter = this.node.enter().append("g")
.attr("class", "node")
.on("dblclick", function(d) { d3.select(this).classed("fixed", d.fixed = false); })
.call(this.drag);
nodeEnter.append("image")
.attr("xlink:href", "./router.svg")
.attr("x", -CONF.image.width/2)
.attr("y", -CONF.image.height/2)
.attr("width", CONF.image.width)
.attr("height", CONF.image.height);
nodeEnter.append("text")
.attr("dx", -CONF.image.width/2)
.attr("dy", CONF.image.height-10)
.text(function(d) { return "dpid: " + trim_zero(d.dpid); });
var ports = topo.get_ports();
this.port.remove();
this.port = this.svg.selectAll(".port").data(ports);
var portEnter = this.port.enter().append("g")
.attr("class", "port");
portEnter.append("circle")
.attr("r", 8);
portEnter.append("text")
.attr("dx", -3)
.attr("dy", 3)
.text(function(d) { return trim_zero(d.port_no); });
};
function is_valid_link(link) {
return (link.src.dpid < link.dst.dpid)
}
var topo = {
nodes: [],
links: [],
node_index: {}, // dpid -> index of nodes array
initialize: function (data) {
this.add_nodes(data.switches);
this.add_links(data.links);
},
add_nodes: function (nodes) {
for (var i = 0; i < nodes.length; i++) {
this.nodes.push(nodes[i]);
}
this.refresh_node_index();
},
add_links: function (links) {
for (var i = 0; i < links.length; i++) {
if (!is_valid_link(links[i])) continue;
console.log("add link: " + JSON.stringify(links[i]));
var src_dpid = links[i].src.dpid;
var dst_dpid = links[i].dst.dpid;
var src_index = this.node_index[src_dpid];
var dst_index = this.node_index[dst_dpid];
var link = {
source: src_index,
target: dst_index,
port: {
src: links[i].src,
dst: links[i].dst
}
}
this.links.push(link);
}
},
delete_nodes: function (nodes) {
for (var i = 0; i < nodes.length; i++) {
console.log("delete switch: " + JSON.stringify(nodes[i]));
node_index = this.get_node_index(nodes[i]);
this.nodes.splice(node_index, 1);
}
this.refresh_node_index();
},
delete_links: function (links) {
for (var i = 0; i < links.length; i++) {
if (!is_valid_link(links[i])) continue;
console.log("delete link: " + JSON.stringify(links[i]));
link_index = this.get_link_index(links[i]);
this.links.splice(link_index, 1);
}
},
get_node_index: function (node) {
for (var i = 0; i < this.nodes.length; i++) {
if (node.dpid == this.nodes[i].dpid) {
return i;
}
}
return null;
},
get_link_index: function (link) {
for (var i = 0; i < this.links.length; i++) {
if (link.src.dpid == this.links[i].port.src.dpid &&
link.src.port_no == this.links[i].port.src.port_no &&
link.dst.dpid == this.links[i].port.dst.dpid &&
link.dst.port_no == this.links[i].port.dst.port_no) {
return i;
}
}
return null;
},
get_ports: function () {
var ports = [];
var pushed = {};
for (var i = 0; i < this.links.length; i++) {
function _push(p, dir) {
key = p.dpid + ":" + p.port_no;
if (key in pushed) {
return 0;
}
pushed[key] = true;
p.link_idx = i;
p.link_dir = dir;
return ports.push(p);
}
_push(this.links[i].port.src, "source");
_push(this.links[i].port.dst, "target");
}
return ports;
},
get_port_point: function (d) {
var weight = 0.88;
var link = this.links[d.link_idx];
var x1 = link.source.x;
var y1 = link.source.y;
var x2 = link.target.x;
var y2 = link.target.y;
if (d.link_dir == "target") weight = 1.0 - weight;
var x = x1 * weight + x2 * (1.0 - weight);
var y = y1 * weight + y2 * (1.0 - weight);
return {x: x, y: y};
},
refresh_node_index: function(){
this.node_index = {};
for (var i = 0; i < this.nodes.length; i++) {
this.node_index[this.nodes[i].dpid] = i;
}
},
}
var rpc = {
event_switch_enter: function (params) {
var switches = [];
for(var i=0; i < params.length; i++){
switches.push({"dpid":params[i].dpid,"ports":params[i].ports});
}
topo.add_nodes(switches);
elem.update();
return "";
},
event_switch_leave: function (params) {
var switches = [];
for(var i=0; i < params.length; i++){
switches.push({"dpid":params[i].dpid,"ports":params[i].ports});
}
topo.delete_nodes(switches);
elem.update();
return "";
},
event_link_add: function (links) {
topo.add_links(links);
elem.update();
return "";
},
event_link_delete: function (links) {
topo.delete_links(links);
elem.update();
return "";
},
}
function initialize_topology() {
d3.json("/v1.0/topology/switches", function(error, switches) {
d3.json("/v1.0/topology/links", function(error, links) {
topo.initialize({switches: switches, links: links});
elem.update();
});
});
}
function main() {
initialize_topology();
}
main();

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="38pt" height="26pt" viewBox="0 0 38 26" version="1.1">
<defs>
<clipPath id="clip1">
<path d="M 0.0585938 0.820312 L 37 0.820312 L 37 25.820312 L 0.0585938 25.820312 L 0.0585938 0.820312 Z M 0.0585938 0.820312 "/>
</clipPath>
<clipPath id="clip2">
<path d="M 0.0585938 0.820312 L 37 0.820312 L 37 25.820312 L 0.0585938 25.820312 L 0.0585938 0.820312 Z M 0.0585938 0.820312 "/>
</clipPath>
</defs>
<g id="surface0">
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0.784314%,42.352941%,60.784314%);fill-opacity:1;" d="M 36.984375 8.171875 C 36.984375 12.121094 28.75 15.324219 18.59375 15.324219 C 8.433594 15.324219 0.199219 12.121094 0.199219 8.171875 L 0.199219 18.648438 C 0.199219 22.597656 8.433594 25.800781 18.59375 25.800781 C 28.75 25.800781 36.984375 22.597656 36.984375 18.648438 L 36.984375 8.171875 "/>
<g clip-path="url(#clip1)" clip-rule="nonzero">
<path style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,100%,100%);stroke-opacity:1;stroke-miterlimit:4;" d="M 36.984375 17.828125 C 36.984375 13.878906 28.75 10.675781 18.59375 10.675781 C 8.433594 10.675781 0.199219 13.878906 0.199219 17.828125 L 0.199219 7.351562 C 0.199219 3.402344 8.433594 0.199219 18.59375 0.199219 C 28.75 0.199219 36.984375 3.402344 36.984375 7.351562 L 36.984375 17.828125 Z M 36.984375 17.828125 " transform="matrix(1,0,0,-1,0,26)"/>
</g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0.784314%,42.352941%,60.784314%);fill-opacity:1;" d="M 18.59375 15.324219 C 28.75 15.324219 36.984375 12.121094 36.984375 8.171875 C 36.984375 4.222656 28.75 1.019531 18.59375 1.019531 C 8.433594 1.019531 0.199219 4.222656 0.199219 8.171875 C 0.199219 12.121094 8.433594 15.324219 18.59375 15.324219 "/>
<g clip-path="url(#clip2)" clip-rule="nonzero">
<path style="fill:none;stroke-width:0.4;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,100%,100%);stroke-opacity:1;stroke-miterlimit:4;" d="M 18.59375 10.675781 C 28.75 10.675781 36.984375 13.878906 36.984375 17.828125 C 36.984375 21.777344 28.75 24.980469 18.59375 24.980469 C 8.433594 24.980469 0.199219 21.777344 0.199219 17.828125 C 0.199219 13.878906 8.433594 10.675781 18.59375 10.675781 Z M 18.59375 10.675781 " transform="matrix(1,0,0,-1,0,26)"/>
</g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 14.394531 5.375 L 15.910156 7.652344 L 10.167969 8.980469 L 11.425781 7.9375 L 2.550781 6.417969 L 4.773438 4.75 L 13.339844 6.199219 L 14.394531 5.375 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 22.472656 10.898438 L 21.4375 8.550781 L 26.617188 7.515625 L 25.71875 8.320312 L 34.351562 9.796875 L 32.277344 11.453125 L 23.699219 9.839844 L 22.472656 10.898438 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 19.640625 4.132812 L 25.441406 2.542969 L 25.511719 5.027344 L 24.058594 4.753906 L 21.230469 7.101562 L 18.527344 6.707031 L 21.449219 4.410156 L 19.640625 4.132812 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 17.152344 13.039062 L 11.628906 14.074219 L 11.421875 11.519531 L 13.011719 11.867188 L 16.050781 9.269531 L 18.742188 9.726562 L 15.496094 12.558594 L 17.152344 13.039062 "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -1,778 +0,0 @@
# Copyright (C) 2012 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 logging
import json
import ast
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller import dpset
from os_ken.controller.handler import MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.exception import OSKenException
from os_ken.ofproto import ofproto_v1_0
from os_ken.ofproto import ofproto_v1_2
from os_ken.ofproto import ofproto_v1_3
from os_ken.ofproto import ofproto_v1_4
from os_ken.ofproto import ofproto_v1_5
from os_ken.lib import ofctl_v1_0
from os_ken.lib import ofctl_v1_2
from os_ken.lib import ofctl_v1_3
from os_ken.lib import ofctl_v1_4
from os_ken.lib import ofctl_v1_5
from os_ken.app.wsgi import ControllerBase
from os_ken.app.wsgi import Response
from os_ken.app.wsgi import WSGIApplication
LOG = logging.getLogger('os_ken.app.ofctl_rest')
# supported ofctl versions in this restful app
supported_ofctl = {
ofproto_v1_0.OFP_VERSION: ofctl_v1_0,
ofproto_v1_2.OFP_VERSION: ofctl_v1_2,
ofproto_v1_3.OFP_VERSION: ofctl_v1_3,
ofproto_v1_4.OFP_VERSION: ofctl_v1_4,
ofproto_v1_5.OFP_VERSION: ofctl_v1_5,
}
# REST API
#
# Retrieve the switch stats
#
# get the list of all switches
# GET /stats/switches
#
# get the desc stats of the switch
# GET /stats/desc/<dpid>
#
# get flows desc stats of the switch
# GET /stats/flowdesc/<dpid>
#
# get flows desc stats of the switch filtered by the fields
# POST /stats/flowdesc/<dpid>
#
# get flows stats of the switch
# GET /stats/flow/<dpid>
#
# get flows stats of the switch filtered by the fields
# POST /stats/flow/<dpid>
#
# get aggregate flows stats of the switch
# GET /stats/aggregateflow/<dpid>
#
# get aggregate flows stats of the switch filtered by the fields
# POST /stats/aggregateflow/<dpid>
#
# get table stats of the switch
# GET /stats/table/<dpid>
#
# get table features stats of the switch
# GET /stats/tablefeatures/<dpid>
#
# get ports stats of the switch
# GET /stats/port/<dpid>[/<port>]
# Note: Specification of port number is optional
#
# get queues stats of the switch
# GET /stats/queue/<dpid>[/<port>[/<queue_id>]]
# Note: Specification of port number and queue id are optional
# If you want to omitting the port number and setting the queue id,
# please specify the keyword "ALL" to the port number
# e.g. GET /stats/queue/1/ALL/1
#
# get queues config stats of the switch
# GET /stats/queueconfig/<dpid>[/<port>]
# Note: Specification of port number is optional
#
# get queues desc stats of the switch
# GET /stats/queuedesc/<dpid>[/<port>[/<queue_id>]]
# Note: Specification of port number and queue id are optional
# If you want to omitting the port number and setting the queue id,
# please specify the keyword "ALL" to the port number
# e.g. GET /stats/queuedesc/1/ALL/1
#
# get meter features stats of the switch
# GET /stats/meterfeatures/<dpid>
#
# get meter config stats of the switch
# GET /stats/meterconfig/<dpid>[/<meter_id>]
# Note: Specification of meter id is optional
#
# get meter desc stats of the switch
# GET /stats/meterdesc/<dpid>[/<meter_id>]
# Note: Specification of meter id is optional
#
# get meters stats of the switch
# GET /stats/meter/<dpid>[/<meter_id>]
# Note: Specification of meter id is optional
#
# get group features stats of the switch
# GET /stats/groupfeatures/<dpid>
#
# get groups desc stats of the switch
# GET /stats/groupdesc/<dpid>[/<group_id>]
# Note: Specification of group id is optional (OpenFlow 1.5 or later)
#
# get groups stats of the switch
# GET /stats/group/<dpid>[/<group_id>]
# Note: Specification of group id is optional
#
# get ports description of the switch
# GET /stats/portdesc/<dpid>[/<port_no>]
# Note: Specification of port number is optional (OpenFlow 1.5 or later)
# Update the switch stats
#
# add a flow entry
# POST /stats/flowentry/add
#
# modify all matching flow entries
# POST /stats/flowentry/modify
#
# modify flow entry strictly matching wildcards and priority
# POST /stats/flowentry/modify_strict
#
# delete all matching flow entries
# POST /stats/flowentry/delete
#
# delete flow entry strictly matching wildcards and priority
# POST /stats/flowentry/delete_strict
#
# delete all flow entries of the switch
# DELETE /stats/flowentry/clear/<dpid>
#
# add a meter entry
# POST /stats/meterentry/add
#
# modify a meter entry
# POST /stats/meterentry/modify
#
# delete a meter entry
# POST /stats/meterentry/delete
#
# add a group entry
# POST /stats/groupentry/add
#
# modify a group entry
# POST /stats/groupentry/modify
#
# delete a group entry
# POST /stats/groupentry/delete
#
# modify behavior of the physical port
# POST /stats/portdesc/modify
#
# modify role of controller
# POST /stats/role
#
#
# send a experimeter message
# POST /stats/experimenter/<dpid>
class CommandNotFoundError(OSKenException):
message = 'No such command : %(cmd)s'
class PortNotFoundError(OSKenException):
message = 'No such port info: %(port_no)s'
def stats_method(method):
def wrapper(self, req, dpid, *args, **kwargs):
# Get datapath instance from DPSet
try:
dp = self.dpset.get(int(str(dpid), 0))
except ValueError:
LOG.exception('Invalid dpid: %s', dpid)
return Response(status=400)
if dp is None:
LOG.error('No such Datapath: %s', dpid)
return Response(status=404)
# Get lib/ofctl_* module
try:
ofctl = supported_ofctl.get(dp.ofproto.OFP_VERSION)
except KeyError:
LOG.exception('Unsupported OF version: %s',
dp.ofproto.OFP_VERSION)
return Response(status=501)
# Invoke StatsController method
try:
ret = method(self, req, dp, ofctl, *args, **kwargs)
return Response(content_type='application/json',
body=json.dumps(ret))
except ValueError:
LOG.exception('Invalid syntax: %s', req.body)
return Response(status=400)
except AttributeError:
LOG.exception('Unsupported OF request in this version: %s',
dp.ofproto.OFP_VERSION)
return Response(status=501)
return wrapper
def command_method(method):
def wrapper(self, req, *args, **kwargs):
# Parse request json body
try:
if req.body:
# We use ast.literal_eval() to parse request json body
# instead of json.loads().
# Because we need to parse binary format body
# in send_experimenter().
body = ast.literal_eval(req.body.decode('utf-8'))
else:
body = {}
except SyntaxError:
LOG.exception('Invalid syntax: %s', req.body)
return Response(status=400)
# Get datapath_id from request parameters
dpid = body.get('dpid', None)
if not dpid:
try:
dpid = kwargs.pop('dpid')
except KeyError:
LOG.exception('Cannot get dpid from request parameters')
return Response(status=400)
# Get datapath instance from DPSet
try:
dp = self.dpset.get(int(str(dpid), 0))
except ValueError:
LOG.exception('Invalid dpid: %s', dpid)
return Response(status=400)
if dp is None:
LOG.error('No such Datapath: %s', dpid)
return Response(status=404)
# Get lib/ofctl_* module
try:
ofctl = supported_ofctl.get(dp.ofproto.OFP_VERSION)
except KeyError:
LOG.exception('Unsupported OF version: version=%s',
dp.ofproto.OFP_VERSION)
return Response(status=501)
# Invoke StatsController method
try:
method(self, req, dp, ofctl, body, *args, **kwargs)
return Response(status=200)
except ValueError:
LOG.exception('Invalid syntax: %s', req.body)
return Response(status=400)
except AttributeError:
LOG.exception('Unsupported OF request in this version: %s',
dp.ofproto.OFP_VERSION)
return Response(status=501)
except CommandNotFoundError as e:
LOG.exception(e.message)
return Response(status=404)
except PortNotFoundError as e:
LOG.exception(e.message)
return Response(status=404)
return wrapper
class StatsController(ControllerBase):
def __init__(self, req, link, data, **config):
super(StatsController, self).__init__(req, link, data, **config)
self.dpset = data['dpset']
self.waiters = data['waiters']
def get_dpids(self, req, **_kwargs):
dps = list(self.dpset.dps.keys())
body = json.dumps(dps)
return Response(content_type='application/json', body=body)
@stats_method
def get_desc_stats(self, req, dp, ofctl, **kwargs):
return ofctl.get_desc_stats(dp, self.waiters)
@stats_method
def get_flow_desc(self, req, dp, ofctl, **kwargs):
flow = req.json if req.body else {}
return ofctl.get_flow_desc(dp, self.waiters, flow)
@stats_method
def get_flow_stats(self, req, dp, ofctl, **kwargs):
flow = req.json if req.body else {}
return ofctl.get_flow_stats(dp, self.waiters, flow)
@stats_method
def get_aggregate_flow_stats(self, req, dp, ofctl, **kwargs):
flow = req.json if req.body else {}
return ofctl.get_aggregate_flow_stats(dp, self.waiters, flow)
@stats_method
def get_table_stats(self, req, dp, ofctl, **kwargs):
return ofctl.get_table_stats(dp, self.waiters)
@stats_method
def get_table_features(self, req, dp, ofctl, **kwargs):
return ofctl.get_table_features(dp, self.waiters)
@stats_method
def get_port_stats(self, req, dp, ofctl, port=None, **kwargs):
if port == "ALL":
port = None
return ofctl.get_port_stats(dp, self.waiters, port)
@stats_method
def get_queue_stats(self, req, dp, ofctl,
port=None, queue_id=None, **kwargs):
if port == "ALL":
port = None
if queue_id == "ALL":
queue_id = None
return ofctl.get_queue_stats(dp, self.waiters, port, queue_id)
@stats_method
def get_queue_config(self, req, dp, ofctl, port=None, **kwargs):
if port == "ALL":
port = None
return ofctl.get_queue_config(dp, self.waiters, port)
@stats_method
def get_queue_desc(self, req, dp, ofctl,
port=None, queue=None, **_kwargs):
if port == "ALL":
port = None
if queue == "ALL":
queue = None
return ofctl.get_queue_desc(dp, self.waiters, port, queue)
@stats_method
def get_meter_features(self, req, dp, ofctl, **kwargs):
return ofctl.get_meter_features(dp, self.waiters)
@stats_method
def get_meter_config(self, req, dp, ofctl, meter_id=None, **kwargs):
if meter_id == "ALL":
meter_id = None
return ofctl.get_meter_config(dp, self.waiters, meter_id)
@stats_method
def get_meter_desc(self, req, dp, ofctl, meter_id=None, **kwargs):
if meter_id == "ALL":
meter_id = None
return ofctl.get_meter_desc(dp, self.waiters, meter_id)
@stats_method
def get_meter_stats(self, req, dp, ofctl, meter_id=None, **kwargs):
if meter_id == "ALL":
meter_id = None
return ofctl.get_meter_stats(dp, self.waiters, meter_id)
@stats_method
def get_group_features(self, req, dp, ofctl, **kwargs):
return ofctl.get_group_features(dp, self.waiters)
@stats_method
def get_group_desc(self, req, dp, ofctl, group_id=None, **kwargs):
if dp.ofproto.OFP_VERSION < ofproto_v1_5.OFP_VERSION:
return ofctl.get_group_desc(dp, self.waiters)
else:
return ofctl.get_group_desc(dp, self.waiters, group_id)
@stats_method
def get_group_stats(self, req, dp, ofctl, group_id=None, **kwargs):
if group_id == "ALL":
group_id = None
return ofctl.get_group_stats(dp, self.waiters, group_id)
@stats_method
def get_port_desc(self, req, dp, ofctl, port_no=None, **kwargs):
if dp.ofproto.OFP_VERSION < ofproto_v1_5.OFP_VERSION:
return ofctl.get_port_desc(dp, self.waiters)
else:
return ofctl.get_port_desc(dp, self.waiters, port_no)
@stats_method
def get_role(self, req, dp, ofctl, **kwargs):
return ofctl.get_role(dp, self.waiters)
@command_method
def mod_flow_entry(self, req, dp, ofctl, flow, cmd, **kwargs):
cmd_convert = {
'add': dp.ofproto.OFPFC_ADD,
'modify': dp.ofproto.OFPFC_MODIFY,
'modify_strict': dp.ofproto.OFPFC_MODIFY_STRICT,
'delete': dp.ofproto.OFPFC_DELETE,
'delete_strict': dp.ofproto.OFPFC_DELETE_STRICT,
}
mod_cmd = cmd_convert.get(cmd, None)
if mod_cmd is None:
raise CommandNotFoundError(cmd=cmd)
ofctl.mod_flow_entry(dp, flow, mod_cmd)
@command_method
def delete_flow_entry(self, req, dp, ofctl, flow, **kwargs):
if ofproto_v1_0.OFP_VERSION == dp.ofproto.OFP_VERSION:
flow = {}
else:
flow = {'table_id': dp.ofproto.OFPTT_ALL}
ofctl.mod_flow_entry(dp, flow, dp.ofproto.OFPFC_DELETE)
@command_method
def mod_meter_entry(self, req, dp, ofctl, meter, cmd, **kwargs):
cmd_convert = {
'add': dp.ofproto.OFPMC_ADD,
'modify': dp.ofproto.OFPMC_MODIFY,
'delete': dp.ofproto.OFPMC_DELETE,
}
mod_cmd = cmd_convert.get(cmd, None)
if mod_cmd is None:
raise CommandNotFoundError(cmd=cmd)
ofctl.mod_meter_entry(dp, meter, mod_cmd)
@command_method
def mod_group_entry(self, req, dp, ofctl, group, cmd, **kwargs):
cmd_convert = {
'add': dp.ofproto.OFPGC_ADD,
'modify': dp.ofproto.OFPGC_MODIFY,
'delete': dp.ofproto.OFPGC_DELETE,
}
mod_cmd = cmd_convert.get(cmd, None)
if mod_cmd is None:
raise CommandNotFoundError(cmd=cmd)
ofctl.mod_group_entry(dp, group, mod_cmd)
@command_method
def mod_port_behavior(self, req, dp, ofctl, port_config, cmd, **kwargs):
port_no = port_config.get('port_no', None)
port_no = int(str(port_no), 0)
port_info = self.dpset.port_state[int(dp.id)].get(port_no)
if port_info:
port_config.setdefault('hw_addr', port_info.hw_addr)
if dp.ofproto.OFP_VERSION < ofproto_v1_4.OFP_VERSION:
port_config.setdefault('advertise', port_info.advertised)
else:
port_config.setdefault('properties', port_info.properties)
else:
raise PortNotFoundError(port_no=port_no)
if cmd != 'modify':
raise CommandNotFoundError(cmd=cmd)
ofctl.mod_port_behavior(dp, port_config)
@command_method
def send_experimenter(self, req, dp, ofctl, exp, **kwargs):
ofctl.send_experimenter(dp, exp)
@command_method
def set_role(self, req, dp, ofctl, role, **kwargs):
ofctl.set_role(dp, role)
class RestStatsApi(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION,
ofproto_v1_2.OFP_VERSION,
ofproto_v1_3.OFP_VERSION,
ofproto_v1_4.OFP_VERSION,
ofproto_v1_5.OFP_VERSION]
_CONTEXTS = {
'dpset': dpset.DPSet,
'wsgi': WSGIApplication
}
def __init__(self, *args, **kwargs):
super(RestStatsApi, self).__init__(*args, **kwargs)
self.dpset = kwargs['dpset']
wsgi = kwargs['wsgi']
self.waiters = {}
self.data = {}
self.data['dpset'] = self.dpset
self.data['waiters'] = self.waiters
mapper = wsgi.mapper
wsgi.registory['StatsController'] = self.data
path = '/stats'
uri = path + '/switches'
mapper.connect('stats', uri,
controller=StatsController, action='get_dpids',
conditions=dict(method=['GET']))
uri = path + '/desc/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_desc_stats',
conditions=dict(method=['GET']))
uri = path + '/flowdesc/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_flow_stats',
conditions=dict(method=['GET', 'POST']))
uri = path + '/flow/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_flow_stats',
conditions=dict(method=['GET', 'POST']))
uri = path + '/aggregateflow/{dpid}'
mapper.connect('stats', uri,
controller=StatsController,
action='get_aggregate_flow_stats',
conditions=dict(method=['GET', 'POST']))
uri = path + '/table/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_table_stats',
conditions=dict(method=['GET']))
uri = path + '/tablefeatures/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_table_features',
conditions=dict(method=['GET']))
uri = path + '/port/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_port_stats',
conditions=dict(method=['GET']))
uri = path + '/port/{dpid}/{port}'
mapper.connect('stats', uri,
controller=StatsController, action='get_port_stats',
conditions=dict(method=['GET']))
uri = path + '/queue/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_queue_stats',
conditions=dict(method=['GET']))
uri = path + '/queue/{dpid}/{port}'
mapper.connect('stats', uri,
controller=StatsController, action='get_queue_stats',
conditions=dict(method=['GET']))
uri = path + '/queue/{dpid}/{port}/{queue_id}'
mapper.connect('stats', uri,
controller=StatsController, action='get_queue_stats',
conditions=dict(method=['GET']))
uri = path + '/queueconfig/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_queue_config',
conditions=dict(method=['GET']))
uri = path + '/queueconfig/{dpid}/{port}'
mapper.connect('stats', uri,
controller=StatsController, action='get_queue_config',
conditions=dict(method=['GET']))
uri = path + '/queuedesc/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_queue_desc',
conditions=dict(method=['GET']))
uri = path + '/queuedesc/{dpid}/{port}'
mapper.connect('stats', uri,
controller=StatsController, action='get_queue_desc',
conditions=dict(method=['GET']))
uri = path + '/queuedesc/{dpid}/{port}/{queue}'
mapper.connect('stats', uri,
controller=StatsController, action='get_queue_desc',
conditions=dict(method=['GET']))
uri = path + '/meterfeatures/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_meter_features',
conditions=dict(method=['GET']))
uri = path + '/meterconfig/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_meter_config',
conditions=dict(method=['GET']))
uri = path + '/meterconfig/{dpid}/{meter_id}'
mapper.connect('stats', uri,
controller=StatsController, action='get_meter_config',
conditions=dict(method=['GET']))
uri = path + '/meterdesc/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_meter_desc',
conditions=dict(method=['GET']))
uri = path + '/meterdesc/{dpid}/{meter_id}'
mapper.connect('stats', uri,
controller=StatsController, action='get_meter_desc',
conditions=dict(method=['GET']))
uri = path + '/meter/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_meter_stats',
conditions=dict(method=['GET']))
uri = path + '/meter/{dpid}/{meter_id}'
mapper.connect('stats', uri,
controller=StatsController, action='get_meter_stats',
conditions=dict(method=['GET']))
uri = path + '/groupfeatures/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_group_features',
conditions=dict(method=['GET']))
uri = path + '/groupdesc/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_group_desc',
conditions=dict(method=['GET']))
uri = path + '/groupdesc/{dpid}/{group_id}'
mapper.connect('stats', uri,
controller=StatsController, action='get_group_desc',
conditions=dict(method=['GET']))
uri = path + '/group/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_group_stats',
conditions=dict(method=['GET']))
uri = path + '/group/{dpid}/{group_id}'
mapper.connect('stats', uri,
controller=StatsController, action='get_group_stats',
conditions=dict(method=['GET']))
uri = path + '/portdesc/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_port_desc',
conditions=dict(method=['GET']))
uri = path + '/portdesc/{dpid}/{port_no}'
mapper.connect('stats', uri,
controller=StatsController, action='get_port_desc',
conditions=dict(method=['GET']))
uri = path + '/role/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_role',
conditions=dict(method=['GET']))
uri = path + '/flowentry/{cmd}'
mapper.connect('stats', uri,
controller=StatsController, action='mod_flow_entry',
conditions=dict(method=['POST']))
uri = path + '/flowentry/clear/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='delete_flow_entry',
conditions=dict(method=['DELETE']))
uri = path + '/meterentry/{cmd}'
mapper.connect('stats', uri,
controller=StatsController, action='mod_meter_entry',
conditions=dict(method=['POST']))
uri = path + '/groupentry/{cmd}'
mapper.connect('stats', uri,
controller=StatsController, action='mod_group_entry',
conditions=dict(method=['POST']))
uri = path + '/portdesc/{cmd}'
mapper.connect('stats', uri,
controller=StatsController, action='mod_port_behavior',
conditions=dict(method=['POST']))
uri = path + '/experimenter/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='send_experimenter',
conditions=dict(method=['POST']))
uri = path + '/role'
mapper.connect('stats', uri,
controller=StatsController, action='set_role',
conditions=dict(method=['POST']))
@set_ev_cls([ofp_event.EventOFPStatsReply,
ofp_event.EventOFPDescStatsReply,
ofp_event.EventOFPFlowStatsReply,
ofp_event.EventOFPAggregateStatsReply,
ofp_event.EventOFPTableStatsReply,
ofp_event.EventOFPTableFeaturesStatsReply,
ofp_event.EventOFPPortStatsReply,
ofp_event.EventOFPQueueStatsReply,
ofp_event.EventOFPQueueDescStatsReply,
ofp_event.EventOFPMeterStatsReply,
ofp_event.EventOFPMeterFeaturesStatsReply,
ofp_event.EventOFPMeterConfigStatsReply,
ofp_event.EventOFPGroupStatsReply,
ofp_event.EventOFPGroupFeaturesStatsReply,
ofp_event.EventOFPGroupDescStatsReply,
ofp_event.EventOFPPortDescStatsReply
], MAIN_DISPATCHER)
def stats_reply_handler(self, ev):
msg = ev.msg
dp = msg.datapath
if dp.id not in self.waiters:
return
if msg.xid not in self.waiters[dp.id]:
return
lock, msgs = self.waiters[dp.id][msg.xid]
msgs.append(msg)
flags = 0
if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION:
flags = dp.ofproto.OFPSF_REPLY_MORE
elif dp.ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION:
flags = dp.ofproto.OFPSF_REPLY_MORE
elif dp.ofproto.OFP_VERSION >= ofproto_v1_3.OFP_VERSION:
flags = dp.ofproto.OFPMPF_REPLY_MORE
if msg.flags & flags:
return
del self.waiters[dp.id][msg.xid]
lock.set()
@set_ev_cls([ofp_event.EventOFPSwitchFeatures,
ofp_event.EventOFPQueueGetConfigReply,
ofp_event.EventOFPRoleReply,
], MAIN_DISPATCHER)
def features_reply_handler(self, ev):
msg = ev.msg
dp = msg.datapath
if dp.id not in self.waiters:
return
if msg.xid not in self.waiters[dp.id]:
return
lock, msgs = self.waiters[dp.id][msg.xid]
msgs.append(msg)
del self.waiters[dp.id][msg.xid]
lock.set()

View File

@ -1,179 +0,0 @@
# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2012 Isaku Yamahata <yamahata at private email ne jp>
#
# 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.
"""
This module provides a set of REST API for switch configuration.
- Per-switch Key-Value store
"""
import json
from six.moves import http_client
from os_ken.app.wsgi import ControllerBase
from os_ken.app.wsgi import Response
from os_ken.base import app_manager
from os_ken.controller import conf_switch
from os_ken.lib import dpid as dpid_lib
# REST API for switch configuration
#
# get all the switches
# GET /v1.0/conf/switches
#
# get all the configuration keys of a switch
# GET /v1.0/conf/switches/<dpid>
#
# delete all the configuration of a switch
# DELETE /v1.0/conf/switches/<dpid>
#
# set the <key> configuration of a switch
# PUT /v1.0/conf/switches/<dpid>/<key>
#
# get the <key> configuration of a switch
# GET /v1.0/conf/switches/<dpid>/<key>
#
# delete the <key> configuration of a switch
# DELETE /v1.0/conf/switches/<dpid>/<key>
#
# where
# <dpid>: datapath id in 16 hex
class ConfSwitchController(ControllerBase):
def __init__(self, req, link, data, **config):
super(ConfSwitchController, self).__init__(req, link, data, **config)
self.conf_switch = data
def list_switches(self, _req, **_kwargs):
dpids = self.conf_switch.dpids()
body = json.dumps([dpid_lib.dpid_to_str(dpid) for dpid in dpids])
return Response(content_type='application/json', body=body)
@staticmethod
def _do_switch(dpid, func, ret_func):
dpid = dpid_lib.str_to_dpid(dpid)
try:
ret = func(dpid)
except KeyError:
return Response(status=http_client.NOT_FOUND,
body='no dpid is found %s' %
dpid_lib.dpid_to_str(dpid))
return ret_func(ret)
def delete_switch(self, _req, dpid, **_kwargs):
def _delete_switch(dpid):
self.conf_switch.del_dpid(dpid)
return None
def _ret(_ret):
return Response(status=http_client.ACCEPTED)
return self._do_switch(dpid, _delete_switch, _ret)
def list_keys(self, _req, dpid, **_kwargs):
def _list_keys(dpid):
return self.conf_switch.keys(dpid)
def _ret(keys):
body = json.dumps(keys)
return Response(content_type='application/json', body=body)
return self._do_switch(dpid, _list_keys, _ret)
@staticmethod
def _do_key(dpid, key, func, ret_func):
dpid = dpid_lib.str_to_dpid(dpid)
try:
ret = func(dpid, key)
except KeyError:
return Response(status=http_client.NOT_FOUND,
body='no dpid/key is found %s %s' %
(dpid_lib.dpid_to_str(dpid), key))
return ret_func(ret)
def set_key(self, req, dpid, key, **_kwargs):
def _set_val(dpid, key):
try:
val = req.json if req.body else {}
except ValueError:
return Response(status=http_client.BAD_REQUEST,
body='invalid syntax %s' % req.body)
self.conf_switch.set_key(dpid, key, val)
return None
def _ret(_ret):
return Response(status=http_client.CREATED)
return self._do_key(dpid, key, _set_val, _ret)
def get_key(self, _req, dpid, key, **_kwargs):
def _get_key(dpid, key):
return self.conf_switch.get_key(dpid, key)
def _ret(val):
return Response(content_type='application/json',
body=json.dumps(val))
return self._do_key(dpid, key, _get_key, _ret)
def delete_key(self, _req, dpid, key, **_kwargs):
def _delete_key(dpid, key):
self.conf_switch.del_key(dpid, key)
return None
def _ret(_ret):
return Response()
return self._do_key(dpid, key, _delete_key, _ret)
class ConfSwitchAPI(app_manager.OSKenApp):
_CONTEXTS = {
'conf_switch': conf_switch.ConfSwitchSet,
}
def __init__(self, *args, **kwargs):
super(ConfSwitchAPI, self).__init__(*args, **kwargs)
self.conf_switch = kwargs['conf_switch']
wsgi = kwargs['wsgi']
mapper = wsgi.mapper
controller = ConfSwitchController
wsgi.registory[controller.__name__] = self.conf_switch
route_name = 'conf_switch'
uri = '/v1.0/conf/switches'
mapper.connect(route_name, uri, controller=controller,
action='list_switches',
conditions=dict(method=['GET']))
uri += '/{dpid}'
requirements = {'dpid': dpid_lib.DPID_PATTERN}
s = mapper.submapper(controller=controller, requirements=requirements)
s.connect(route_name, uri, action='delete_switch',
conditions=dict(method=['DELETE']))
s.connect(route_name, uri, action='list_keys',
conditions=dict(method=['GET']))
uri += '/{key}'
s.connect(route_name, uri, action='set_key',
conditions=dict(method=['PUT']))
s.connect(route_name, uri, action='get_key',
conditions=dict(method=['GET']))
s.connect(route_name, uri, action='delete_key',
conditions=dict(method=['DELETE']))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,119 +0,0 @@
# 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 os_ken.app.wsgi import ControllerBase
from os_ken.app.wsgi import Response
from os_ken.app.wsgi import route
from os_ken.app.wsgi import WSGIApplication
from os_ken.base import app_manager
from os_ken.lib import dpid as dpid_lib
from os_ken.topology.api import get_switch, get_link, get_host
# 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>
#
# get all the hosts
# GET /v1.0/topology/hosts
#
# get the hosts of a switch
# GET /v1.0/topology/hosts/<dpid>
#
# where
# <dpid>: datapath id in 16 hex
class TopologyAPI(app_manager.OSKenApp):
_CONTEXTS = {
'wsgi': WSGIApplication
}
def __init__(self, *args, **kwargs):
super(TopologyAPI, self).__init__(*args, **kwargs)
wsgi = kwargs['wsgi']
wsgi.register(TopologyController, {'topology_api_app': self})
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']
@route('topology', '/v1.0/topology/switches',
methods=['GET'])
def list_switches(self, req, **kwargs):
return self._switches(req, **kwargs)
@route('topology', '/v1.0/topology/switches/{dpid}',
methods=['GET'], requirements={'dpid': dpid_lib.DPID_PATTERN})
def get_switch(self, req, **kwargs):
return self._switches(req, **kwargs)
@route('topology', '/v1.0/topology/links',
methods=['GET'])
def list_links(self, req, **kwargs):
return self._links(req, **kwargs)
@route('topology', '/v1.0/topology/links/{dpid}',
methods=['GET'], requirements={'dpid': dpid_lib.DPID_PATTERN})
def get_links(self, req, **kwargs):
return self._links(req, **kwargs)
@route('topology', '/v1.0/topology/hosts',
methods=['GET'])
def list_hosts(self, req, **kwargs):
return self._hosts(req, **kwargs)
@route('topology', '/v1.0/topology/hosts/{dpid}',
methods=['GET'], requirements={'dpid': dpid_lib.DPID_PATTERN})
def get_hosts(self, req, **kwargs):
return self._hosts(req, **kwargs)
def _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 _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)
def _hosts(self, req, **kwargs):
dpid = None
if 'dpid' in kwargs:
dpid = dpid_lib.str_to_dpid(kwargs['dpid'])
hosts = get_host(self.topology_api_app, dpid)
body = json.dumps([host.to_dict() for host in hosts])
return Response(content_type='application/json', body=body)

File diff suppressed because it is too large Load Diff

View File

@ -1,95 +0,0 @@
# Copyright (C) 2016 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.
from operator import attrgetter
from os_ken.app import simple_switch_13
from os_ken.controller import ofp_event
from os_ken.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.lib import hub
class SimpleMonitor13(simple_switch_13.SimpleSwitch13):
def __init__(self, *args, **kwargs):
super(SimpleMonitor13, self).__init__(*args, **kwargs)
self.datapaths = {}
self.monitor_thread = hub.spawn(self._monitor)
@set_ev_cls(ofp_event.EventOFPStateChange,
[MAIN_DISPATCHER, DEAD_DISPATCHER])
def _state_change_handler(self, ev):
datapath = ev.datapath
if ev.state == MAIN_DISPATCHER:
if datapath.id not in self.datapaths:
self.logger.debug('register datapath: %016x', datapath.id)
self.datapaths[datapath.id] = datapath
elif ev.state == DEAD_DISPATCHER:
if datapath.id in self.datapaths:
self.logger.debug('unregister datapath: %016x', datapath.id)
del self.datapaths[datapath.id]
def _monitor(self):
while True:
for dp in self.datapaths.values():
self._request_stats(dp)
hub.sleep(10)
def _request_stats(self, datapath):
self.logger.debug('send stats request: %016x', datapath.id)
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
req = parser.OFPFlowStatsRequest(datapath)
datapath.send_msg(req)
req = parser.OFPPortStatsRequest(datapath, 0, ofproto.OFPP_ANY)
datapath.send_msg(req)
@set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER)
def _flow_stats_reply_handler(self, ev):
body = ev.msg.body
self.logger.info('datapath '
'in-port eth-dst '
'out-port packets bytes')
self.logger.info('---------------- '
'-------- ----------------- '
'-------- -------- --------')
for stat in sorted([flow for flow in body if flow.priority == 1],
key=lambda flow: (flow.match['in_port'],
flow.match['eth_dst'])):
self.logger.info('%016x %8x %17s %8x %8d %8d',
ev.msg.datapath.id,
stat.match['in_port'], stat.match['eth_dst'],
stat.instructions[0].actions[0].port,
stat.packet_count, stat.byte_count)
@set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER)
def _port_stats_reply_handler(self, ev):
body = ev.msg.body
self.logger.info('datapath port '
'rx-pkts rx-bytes rx-error '
'tx-pkts tx-bytes tx-error')
self.logger.info('---------------- -------- '
'-------- -------- -------- '
'-------- -------- --------')
for stat in sorted(body, key=attrgetter('port_no')):
self.logger.info('%016x %8x %8d %8d %8d %8d %8d %8d',
ev.msg.datapath.id, stat.port_no,
stat.rx_packets, stat.rx_bytes, stat.rx_errors,
stat.tx_packets, stat.tx_bytes, stat.tx_errors)

View File

@ -1,110 +0,0 @@
# Copyright (C) 2011 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.
"""
An OpenFlow 1.0 L2 learning switch implementation.
"""
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_0
from os_ken.lib.mac import haddr_to_bin
from os_ken.lib.packet import packet
from os_ken.lib.packet import ethernet
from os_ken.lib.packet import ether_types
class SimpleSwitch(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(SimpleSwitch, self).__init__(*args, **kwargs)
self.mac_to_port = {}
def add_flow(self, datapath, in_port, dst, src, actions):
ofproto = datapath.ofproto
match = datapath.ofproto_parser.OFPMatch(
in_port=in_port,
dl_dst=haddr_to_bin(dst), dl_src=haddr_to_bin(src))
mod = datapath.ofproto_parser.OFPFlowMod(
datapath=datapath, match=match, cookie=0,
command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
priority=ofproto.OFP_DEFAULT_PRIORITY,
flags=ofproto.OFPFF_SEND_FLOW_REM, actions=actions)
datapath.send_msg(mod)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
pkt = packet.Packet(msg.data)
eth = pkt.get_protocol(ethernet.ethernet)
if eth.ethertype == ether_types.ETH_TYPE_LLDP:
# ignore lldp packet
return
dst = eth.dst
src = eth.src
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s", dpid, src, dst, msg.in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = msg.in_port
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
actions = [datapath.ofproto_parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
self.add_flow(datapath, msg.in_port, dst, src, actions)
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data
out = datapath.ofproto_parser.OFPPacketOut(
datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port,
actions=actions, data=data)
datapath.send_msg(out)
@set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
def _port_status_handler(self, ev):
msg = ev.msg
reason = msg.reason
port_no = msg.desc.port_no
ofproto = msg.datapath.ofproto
if reason == ofproto.OFPPR_ADD:
self.logger.info("port added %s", port_no)
elif reason == ofproto.OFPPR_DELETE:
self.logger.info("port deleted %s", port_no)
elif reason == ofproto.OFPPR_MODIFY:
self.logger.info("port modified %s", port_no)
else:
self.logger.info("Illeagal port state %s %s", port_no, reason)

View File

@ -1,93 +0,0 @@
# Copyright (C) 2011 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.
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_2
from os_ken.lib.packet import packet
from os_ken.lib.packet import ethernet
from os_ken.lib.packet import ether_types
class SimpleSwitch12(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(SimpleSwitch12, self).__init__(*args, **kwargs)
self.mac_to_port = {}
def add_flow(self, datapath, port, dst, src, actions):
ofproto = datapath.ofproto
match = datapath.ofproto_parser.OFPMatch(in_port=port,
eth_dst=dst,
eth_src=src)
inst = [datapath.ofproto_parser.OFPInstructionActions(
ofproto.OFPIT_APPLY_ACTIONS, actions)]
mod = datapath.ofproto_parser.OFPFlowMod(
datapath=datapath, cookie=0, cookie_mask=0, table_id=0,
command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
priority=0, buffer_id=ofproto.OFP_NO_BUFFER,
out_port=ofproto.OFPP_ANY,
out_group=ofproto.OFPG_ANY,
flags=0, match=match, instructions=inst)
datapath.send_msg(mod)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
in_port = msg.match['in_port']
pkt = packet.Packet(msg.data)
eth = pkt.get_protocols(ethernet.ethernet)[0]
if eth.ethertype == ether_types.ETH_TYPE_LLDP:
# ignore lldp packet
return
dst = eth.dst
src = eth.src
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = in_port
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
actions = [datapath.ofproto_parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
self.add_flow(datapath, in_port, dst, src, actions)
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data
out = datapath.ofproto_parser.OFPPacketOut(
datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port,
actions=actions, data=data)
datapath.send_msg(out)

View File

@ -1,119 +0,0 @@
# Copyright (C) 2011 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.
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_3
from os_ken.lib.packet import packet
from os_ken.lib.packet import ethernet
from os_ken.lib.packet import ether_types
class SimpleSwitch13(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(SimpleSwitch13, self).__init__(*args, **kwargs)
self.mac_to_port = {}
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# install table-miss flow entry
#
# We specify NO BUFFER to max_len of the output action due to
# OVS bug. At this moment, if we specify a lesser number, e.g.,
# 128, OVS will send Packet-In with invalid buffer_id and
# truncated packet data. In that case, we cannot output packets
# correctly. The bug has been fixed in OVS v2.1.0.
match = parser.OFPMatch()
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
ofproto.OFPCML_NO_BUFFER)]
self.add_flow(datapath, 0, match, actions)
def add_flow(self, datapath, priority, match, actions, buffer_id=None):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
actions)]
if buffer_id:
mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id,
priority=priority, match=match,
instructions=inst)
else:
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
match=match, instructions=inst)
datapath.send_msg(mod)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
# If you hit this you might want to increase
# the "miss_send_length" of your switch
if ev.msg.msg_len < ev.msg.total_len:
self.logger.debug("packet truncated: only %s of %s bytes",
ev.msg.msg_len, ev.msg.total_len)
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
in_port = msg.match['in_port']
pkt = packet.Packet(msg.data)
eth = pkt.get_protocols(ethernet.ethernet)[0]
if eth.ethertype == ether_types.ETH_TYPE_LLDP:
# ignore lldp packet
return
dst = eth.dst
src = eth.src
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = in_port
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
actions = [parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
# verify if we have a valid buffer_id, if yes avoid to send both
# flow_mod & packet_out
if msg.buffer_id != ofproto.OFP_NO_BUFFER:
self.add_flow(datapath, 1, match, actions, msg.buffer_id)
return
else:
self.add_flow(datapath, 1, match, actions)
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
in_port=in_port, actions=actions, data=data)
datapath.send_msg(out)

View File

@ -1,105 +0,0 @@
# Copyright (C) 2011 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.
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_4
from os_ken.lib.packet import packet
from os_ken.lib.packet import ethernet
from os_ken.lib.packet import ether_types
class SimpleSwitch14(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_4.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(SimpleSwitch14, self).__init__(*args, **kwargs)
self.mac_to_port = {}
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# install table-miss flow entry
#
# We specify NO BUFFER to max_len of the output action due to
# OVS bug. At this moment, if we specify a lesser number, e.g.,
# 128, OVS will send Packet-In with invalid buffer_id and
# truncated packet data. In that case, we cannot output packets
# correctly. The bug has been fixed in OVS v2.1.0.
match = parser.OFPMatch()
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
ofproto.OFPCML_NO_BUFFER)]
self.add_flow(datapath, 0, match, actions)
def add_flow(self, datapath, priority, match, actions):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
actions)]
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
match=match, instructions=inst)
datapath.send_msg(mod)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
in_port = msg.match['in_port']
pkt = packet.Packet(msg.data)
eth = pkt.get_protocols(ethernet.ethernet)[0]
if eth.ethertype == ether_types.ETH_TYPE_LLDP:
# ignore lldp packet
return
dst = eth.dst
src = eth.src
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = in_port
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
actions = [parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
self.add_flow(datapath, 1, match, actions)
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
in_port=in_port, actions=actions, data=data)
datapath.send_msg(out)

View File

@ -1,107 +0,0 @@
# Copyright (C) 2011 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.
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_5
from os_ken.lib.packet import packet
from os_ken.lib.packet import ethernet
from os_ken.lib.packet import ether_types
class SimpleSwitch15(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_5.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(SimpleSwitch15, self).__init__(*args, **kwargs)
self.mac_to_port = {}
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# install table-miss flow entry
#
# We specify NO BUFFER to max_len of the output action due to
# OVS bug. At this moment, if we specify a lesser number, e.g.,
# 128, OVS will send Packet-In with invalid buffer_id and
# truncated packet data. In that case, we cannot output packets
# correctly. The bug has been fixed in OVS v2.1.0.
match = parser.OFPMatch()
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
ofproto.OFPCML_NO_BUFFER)]
self.add_flow(datapath, 0, match, actions)
def add_flow(self, datapath, priority, match, actions):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
actions)]
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
match=match, instructions=inst)
datapath.send_msg(mod)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
in_port = msg.match['in_port']
pkt = packet.Packet(msg.data)
eth = pkt.get_protocols(ethernet.ethernet)[0]
if eth.ethertype == ether_types.ETH_TYPE_LLDP:
# ignore lldp packet
return
dst = eth.dst
src = eth.src
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = in_port
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
actions = [parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
self.add_flow(datapath, 1, match, actions)
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data
match = parser.OFPMatch(in_port=in_port)
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
match=match, actions=actions, data=data)
datapath.send_msg(out)

View File

@ -1,104 +0,0 @@
# 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 struct
from os_ken.base import app_manager
from os_ken.controller.handler import MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_0
from os_ken.lib import addrconv
from os_ken.lib import igmplib
from os_ken.lib.dpid import str_to_dpid
class SimpleSwitchIgmp(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]
_CONTEXTS = {'igmplib': igmplib.IgmpLib}
def __init__(self, *args, **kwargs):
super(SimpleSwitchIgmp, self).__init__(*args, **kwargs)
self.mac_to_port = {}
self._snoop = kwargs['igmplib']
# if you want a switch to operate as a querier,
# set up as follows:
self._snoop.set_querier_mode(
dpid=str_to_dpid('0000000000000001'), server_port=2)
# dpid the datapath id that will operate as a querier.
# server_port a port number which connect to the multicast
# server.
#
# NOTE: you can set up only the one querier.
# when you called this method several times,
# only the last one becomes effective.
def add_flow(self, datapath, in_port, dst, actions):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
match = parser.OFPMatch(in_port=in_port,
dl_dst=addrconv.mac.text_to_bin(dst))
mod = parser.OFPFlowMod(
datapath=datapath, match=match, cookie=0,
command=ofproto.OFPFC_ADD, actions=actions)
datapath.send_msg(mod)
@set_ev_cls(igmplib.EventPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
(dst_, src_, _eth_type) = struct.unpack_from(
'!6s6sH', buffer(msg.data), 0)
src = addrconv.mac.bin_to_text(src_)
dst = addrconv.mac.bin_to_text(dst_)
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s",
dpid, src, dst, msg.in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = msg.in_port
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
actions = [datapath.ofproto_parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
self.add_flow(datapath, msg.in_port, dst, actions)
out = datapath.ofproto_parser.OFPPacketOut(
datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port,
actions=actions)
datapath.send_msg(out)
@set_ev_cls(igmplib.EventMulticastGroupStateChanged,
MAIN_DISPATCHER)
def _status_changed(self, ev):
msg = {
igmplib.MG_GROUP_ADDED: 'Multicast Group Added',
igmplib.MG_MEMBER_CHANGED: 'Multicast Group Member Changed',
igmplib.MG_GROUP_REMOVED: 'Multicast Group Removed',
}
self.logger.info("%s: [%s] querier:[%s] hosts:%s",
msg.get(ev.reason), ev.address, ev.src,
ev.dsts)

View File

@ -1,92 +0,0 @@
# Copyright (C) 2016 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.
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import CONFIG_DISPATCHER
from os_ken.controller.handler import MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_3
from os_ken.lib import igmplib
from os_ken.lib.dpid import str_to_dpid
from os_ken.lib.packet import packet
from os_ken.lib.packet import ethernet
from os_ken.app import simple_switch_13
class SimpleSwitchIgmp13(simple_switch_13.SimpleSwitch13):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
_CONTEXTS = {'igmplib': igmplib.IgmpLib}
def __init__(self, *args, **kwargs):
super(SimpleSwitchIgmp13, self).__init__(*args, **kwargs)
self.mac_to_port = {}
self._snoop = kwargs['igmplib']
self._snoop.set_querier_mode(
dpid=str_to_dpid('0000000000000001'), server_port=2)
@set_ev_cls(igmplib.EventPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
in_port = msg.match['in_port']
pkt = packet.Packet(msg.data)
eth = pkt.get_protocols(ethernet.ethernet)[0]
dst = eth.dst
src = eth.src
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = in_port
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
actions = [parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
self.add_flow(datapath, 1, match, actions)
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
in_port=in_port, actions=actions, data=data)
datapath.send_msg(out)
@set_ev_cls(igmplib.EventMulticastGroupStateChanged,
MAIN_DISPATCHER)
def _status_changed(self, ev):
msg = {
igmplib.MG_GROUP_ADDED: 'Multicast Group Added',
igmplib.MG_MEMBER_CHANGED: 'Multicast Group Member Changed',
igmplib.MG_GROUP_REMOVED: 'Multicast Group Removed',
}
self.logger.info("%s: [%s] querier:[%s] hosts:%s",
msg.get(ev.reason), ev.address, ev.src,
ev.dsts)

View File

@ -1,116 +0,0 @@
# 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 struct
from os_ken.base import app_manager
from os_ken.controller.handler import MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_0
from os_ken.lib import addrconv
from os_ken.lib import lacplib
from os_ken.lib.dpid import str_to_dpid
class SimpleSwitchLacp(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]
_CONTEXTS = {'lacplib': lacplib.LacpLib}
def __init__(self, *args, **kwargs):
super(SimpleSwitchLacp, self).__init__(*args, **kwargs)
self.mac_to_port = {}
self._lacp = kwargs['lacplib']
# in this sample application, bonding i/fs of the switchs
# shall be set up as follows:
# - the port 1 and 2 of the datapath 1 face the slave i/fs.
# - the port 3, 4 and 5 of the datapath 1 face the others.
# - the port 1 and 2 of the datapath 2 face the others.
self._lacp.add(
dpid=str_to_dpid('0000000000000001'), ports=[1, 2])
self._lacp.add(
dpid=str_to_dpid('0000000000000001'), ports=[3, 4, 5])
self._lacp.add(
dpid=str_to_dpid('0000000000000002'), ports=[1, 2])
def add_flow(self, datapath, in_port, dst, actions):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
match = parser.OFPMatch(in_port=in_port,
dl_dst=addrconv.mac.text_to_bin(dst))
mod = parser.OFPFlowMod(
datapath=datapath, match=match, cookie=0,
command=ofproto.OFPFC_ADD, actions=actions)
datapath.send_msg(mod)
def del_flow(self, datapath, dst):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
match = parser.OFPMatch(dl_dst=addrconv.mac.text_to_bin(dst))
mod = parser.OFPFlowMod(
datapath=datapath, match=match, cookie=0,
command=ofproto.OFPFC_DELETE)
datapath.send_msg(mod)
@set_ev_cls(lacplib.EventPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
(dst_, src_, _eth_type) = struct.unpack_from(
'!6s6sH', buffer(msg.data), 0)
src = addrconv.mac.bin_to_text(src_)
dst = addrconv.mac.bin_to_text(dst_)
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s",
dpid, src, dst, msg.in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = msg.in_port
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
actions = [datapath.ofproto_parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
self.add_flow(datapath, msg.in_port, dst, actions)
out = datapath.ofproto_parser.OFPPacketOut(
datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port,
actions=actions)
datapath.send_msg(out)
@set_ev_cls(lacplib.EventSlaveStateChanged, MAIN_DISPATCHER)
def _slave_state_changed_handler(self, ev):
datapath = ev.datapath
dpid = datapath.id
port_no = ev.port
enabled = ev.enabled
self.logger.info("slave state changed port: %d enabled: %s",
port_no, enabled)
if dpid in self.mac_to_port:
for mac in self.mac_to_port[dpid]:
self.del_flow(datapath, mac)
del self.mac_to_port[dpid]
self.mac_to_port.setdefault(dpid, {})

View File

@ -1,106 +0,0 @@
# Copyright (C) 2016 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.
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import CONFIG_DISPATCHER
from os_ken.controller.handler import MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_3
from os_ken.lib import lacplib
from os_ken.lib.dpid import str_to_dpid
from os_ken.lib.packet import packet
from os_ken.lib.packet import ethernet
from os_ken.app import simple_switch_13
class SimpleSwitchLacp13(simple_switch_13.SimpleSwitch13):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
_CONTEXTS = {'lacplib': lacplib.LacpLib}
def __init__(self, *args, **kwargs):
super(SimpleSwitchLacp13, self).__init__(*args, **kwargs)
self.mac_to_port = {}
self._lacp = kwargs['lacplib']
self._lacp.add(
dpid=str_to_dpid('0000000000000001'), ports=[1, 2])
def del_flow(self, datapath, match):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
mod = parser.OFPFlowMod(datapath=datapath,
command=ofproto.OFPFC_DELETE,
out_port=ofproto.OFPP_ANY,
out_group=ofproto.OFPG_ANY,
match=match)
datapath.send_msg(mod)
@set_ev_cls(lacplib.EventPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
in_port = msg.match['in_port']
pkt = packet.Packet(msg.data)
eth = pkt.get_protocols(ethernet.ethernet)[0]
dst = eth.dst
src = eth.src
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = in_port
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
actions = [parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
self.add_flow(datapath, 1, match, actions)
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
in_port=in_port, actions=actions, data=data)
datapath.send_msg(out)
@set_ev_cls(lacplib.EventSlaveStateChanged, MAIN_DISPATCHER)
def _slave_state_changed_handler(self, ev):
datapath = ev.datapath
dpid = datapath.id
port_no = ev.port
enabled = ev.enabled
self.logger.info("slave state changed port: %d enabled: %s",
port_no, enabled)
if dpid in self.mac_to_port:
for mac in self.mac_to_port[dpid]:
match = datapath.ofproto_parser.OFPMatch(eth_dst=mac)
self.del_flow(datapath, match)
del self.mac_to_port[dpid]
self.mac_to_port.setdefault(dpid, {})

View File

@ -1,116 +0,0 @@
# Copyright (C) 2016 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 os_ken.app import simple_switch_13
from os_ken.controller import ofp_event
from os_ken.controller.handler import CONFIG_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.app.wsgi import ControllerBase
from os_ken.app.wsgi import Response
from os_ken.app.wsgi import route
from os_ken.app.wsgi import WSGIApplication
from os_ken.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 = dpid_lib.str_to_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', body=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 = dpid_lib.str_to_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', body=body)
except Exception as e:
return Response(status=500)

View File

@ -1,144 +0,0 @@
# 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 array
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_3
from os_ken.lib.packet import packet
from os_ken.lib.packet import ethernet
from os_ken.lib.packet import ipv4
from os_ken.lib.packet import icmp
from os_ken.lib import snortlib
class SimpleSwitchSnort(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
_CONTEXTS = {'snortlib': snortlib.SnortLib}
def __init__(self, *args, **kwargs):
super(SimpleSwitchSnort, self).__init__(*args, **kwargs)
self.snort = kwargs['snortlib']
self.snort_port = 3
self.mac_to_port = {}
socket_config = {'unixsock': True}
self.snort.set_config(socket_config)
self.snort.start_socket_server()
def packet_print(self, pkt):
pkt = packet.Packet(array.array('B', pkt))
eth = pkt.get_protocol(ethernet.ethernet)
_ipv4 = pkt.get_protocol(ipv4.ipv4)
_icmp = pkt.get_protocol(icmp.icmp)
if _icmp:
self.logger.info("%r", _icmp)
if _ipv4:
self.logger.info("%r", _ipv4)
if eth:
self.logger.info("%r", eth)
# for p in pkt.protocols:
# if hasattr(p, 'protocol_name') is False:
# break
# print('p: %s' % p.protocol_name)
@set_ev_cls(snortlib.EventAlert, MAIN_DISPATCHER)
def _dump_alert(self, ev):
msg = ev.msg
print('alertmsg: %s' % ''.join(msg.alertmsg))
self.packet_print(msg.pkt)
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# install table-miss flow entry
#
# We specify NO BUFFER to max_len of the output action due to
# OVS bug. At this moment, if we specify a lesser number, e.g.,
# 128, OVS will send Packet-In with invalid buffer_id and
# truncated packet data. In that case, we cannot output packets
# correctly.
match = parser.OFPMatch()
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
ofproto.OFPCML_NO_BUFFER)]
self.add_flow(datapath, 0, match, actions)
def add_flow(self, datapath, priority, match, actions):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
actions)]
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
match=match, instructions=inst)
datapath.send_msg(mod)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
in_port = msg.match['in_port']
pkt = packet.Packet(msg.data)
eth = pkt.get_protocols(ethernet.ethernet)[0]
dst = eth.dst
src = eth.src
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
# self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = in_port
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
actions = [parser.OFPActionOutput(out_port),
parser.OFPActionOutput(self.snort_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
self.add_flow(datapath, 1, match, actions)
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
in_port=in_port, actions=actions, data=data)
datapath.send_msg(out)

View File

@ -1,133 +0,0 @@
# 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 struct
from os_ken.base import app_manager
from os_ken.controller.handler import MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_0
from os_ken.lib import dpid as dpid_lib
from os_ken.lib import stplib
from os_ken.lib.mac import haddr_to_str
class SimpleSwitchStp(app_manager.OSKenApp):
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]
_CONTEXTS = {'stplib': stplib.Stp}
def __init__(self, *args, **kwargs):
super(SimpleSwitchStp, self).__init__(*args, **kwargs)
self.mac_to_port = {}
self.stp = kwargs['stplib']
# Sample of stplib config.
# please refer to stplib.Stp.set_config() for details.
"""
config = {dpid_lib.str_to_dpid('0000000000000001'):
{'bridge': {'priority': 0x8000,
'max_age': 10},
'ports': {1: {'priority': 0x80},
2: {'priority': 0x90}}},
dpid_lib.str_to_dpid('0000000000000002'):
{'bridge': {'priority': 0x9000}}}
self.stp.set_config(config)
"""
def add_flow(self, datapath, in_port, dst, actions):
ofproto = datapath.ofproto
wildcards = ofproto_v1_0.OFPFW_ALL
wildcards &= ~ofproto_v1_0.OFPFW_IN_PORT
wildcards &= ~ofproto_v1_0.OFPFW_DL_DST
match = datapath.ofproto_parser.OFPMatch(
wildcards, in_port, 0, dst,
0, 0, 0, 0, 0, 0, 0, 0, 0)
mod = datapath.ofproto_parser.OFPFlowMod(
datapath=datapath, match=match, cookie=0,
command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
priority=ofproto.OFP_DEFAULT_PRIORITY,
flags=ofproto.OFPFF_SEND_FLOW_REM, actions=actions)
datapath.send_msg(mod)
def delete_flow(self, datapath):
ofproto = datapath.ofproto
wildcards = ofproto_v1_0.OFPFW_ALL
match = datapath.ofproto_parser.OFPMatch(
wildcards, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
mod = datapath.ofproto_parser.OFPFlowMod(
datapath=datapath, match=match, cookie=0,
command=ofproto.OFPFC_DELETE)
datapath.send_msg(mod)
@set_ev_cls(stplib.EventPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
dst, src, _eth_type = struct.unpack_from('!6s6sH', buffer(msg.data), 0)
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.debug("packet in %s %s %s %s",
dpid, haddr_to_str(src), haddr_to_str(dst),
msg.in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = msg.in_port
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
actions = [datapath.ofproto_parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
self.add_flow(datapath, msg.in_port, dst, actions)
out = datapath.ofproto_parser.OFPPacketOut(
datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port,
actions=actions)
datapath.send_msg(out)
@set_ev_cls(stplib.EventTopologyChange, MAIN_DISPATCHER)
def _topology_change_handler(self, ev):
dp = ev.dp
dpid_str = dpid_lib.dpid_to_str(dp.id)
msg = 'Receive topology change event. Flush MAC table.'
self.logger.debug("[dpid=%s] %s", dpid_str, msg)
if dp.id in self.mac_to_port:
del self.mac_to_port[dp.id]
self.delete_flow(dp)
@set_ev_cls(stplib.EventPortStateChange, MAIN_DISPATCHER)
def _port_state_change_handler(self, ev):
dpid_str = dpid_lib.dpid_to_str(ev.dp.id)
of_state = {stplib.PORT_STATE_DISABLE: 'DISABLE',
stplib.PORT_STATE_BLOCK: 'BLOCK',
stplib.PORT_STATE_LISTEN: 'LISTEN',
stplib.PORT_STATE_LEARN: 'LEARN',
stplib.PORT_STATE_FORWARD: 'FORWARD'}
self.logger.debug("[dpid=%s][port=%d] state=%s",
dpid_str, ev.port_no, of_state[ev.port_state])

View File

@ -1,121 +0,0 @@
# Copyright (C) 2016 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.
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.ofproto import ofproto_v1_3
from os_ken.lib import dpid as dpid_lib
from os_ken.lib import stplib
from os_ken.lib.packet import packet
from os_ken.lib.packet import ethernet
from os_ken.app import simple_switch_13
class SimpleSwitch13(simple_switch_13.SimpleSwitch13):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
_CONTEXTS = {'stplib': stplib.Stp}
def __init__(self, *args, **kwargs):
super(SimpleSwitch13, self).__init__(*args, **kwargs)
self.mac_to_port = {}
self.stp = kwargs['stplib']
# Sample of stplib config.
# please refer to stplib.Stp.set_config() for details.
config = {dpid_lib.str_to_dpid('0000000000000001'):
{'bridge': {'priority': 0x8000}},
dpid_lib.str_to_dpid('0000000000000002'):
{'bridge': {'priority': 0x9000}},
dpid_lib.str_to_dpid('0000000000000003'):
{'bridge': {'priority': 0xa000}}}
self.stp.set_config(config)
def delete_flow(self, datapath):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
for dst in self.mac_to_port[datapath.id].keys():
match = parser.OFPMatch(eth_dst=dst)
mod = parser.OFPFlowMod(
datapath, command=ofproto.OFPFC_DELETE,
out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY,
priority=1, match=match)
datapath.send_msg(mod)
@set_ev_cls(stplib.EventPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
in_port = msg.match['in_port']
pkt = packet.Packet(msg.data)
eth = pkt.get_protocols(ethernet.ethernet)[0]
dst = eth.dst
src = eth.src
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src] = in_port
if dst in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofproto.OFPP_FLOOD
actions = [parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
self.add_flow(datapath, 1, match, actions)
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
in_port=in_port, actions=actions, data=data)
datapath.send_msg(out)
@set_ev_cls(stplib.EventTopologyChange, MAIN_DISPATCHER)
def _topology_change_handler(self, ev):
dp = ev.dp
dpid_str = dpid_lib.dpid_to_str(dp.id)
msg = 'Receive topology change event. Flush MAC table.'
self.logger.debug("[dpid=%s] %s", dpid_str, msg)
if dp.id in self.mac_to_port:
self.delete_flow(dp)
del self.mac_to_port[dp.id]
@set_ev_cls(stplib.EventPortStateChange, MAIN_DISPATCHER)
def _port_state_change_handler(self, ev):
dpid_str = dpid_lib.dpid_to_str(ev.dp.id)
of_state = {stplib.PORT_STATE_DISABLE: 'DISABLE',
stplib.PORT_STATE_BLOCK: 'BLOCK',
stplib.PORT_STATE_LISTEN: 'LISTEN',
stplib.PORT_STATE_LEARN: 'LEARN',
stplib.PORT_STATE_FORWARD: 'FORWARD'}
self.logger.debug("[dpid=%s][port=%d] state=%s",
dpid_str, ev.port_no, of_state[ev.port_state])

View File

@ -1,99 +0,0 @@
# Copyright (C) 2014 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.
"""
Usage example
Run this application:
$ PYTHONPATH=. ./bin/os_ken run --verbose os_ken.app.simple_switch_websocket_13
Install and run websocket client(in other terminal):
$ pip install websocket-client
$ wsdump.py ws://127.0.0.1:8080/simpleswitch/ws
< "ethernet(dst='ff:ff:ff:ff:ff:ff',ethertype=2054,src='32:1a:51:fb:91:77'), a
rp(dst_ip='10.0.0.2',dst_mac='00:00:00:00:00:00',hlen=6,hwtype=1,opcode=1,plen
=4,proto=2048,src_ip='10.0.0.1',src_mac='32:1a:51:fb:91:77')"
< "ethernet(dst='32:1a:51:fb:91:77',ethertype=2054,src='26:8c:15:0c:de:49'), a
rp(dst_ip='10.0.0.1',dst_mac='32:1a:51:fb:91:77',hlen=6,hwtype=1,opcode=2,plen
=4,proto=2048,src_ip='10.0.0.2',src_mac='26:8c:15:0c:de:49')"
< "ethernet(dst='26:8c:15:0c:de:49',ethertype=2048,src='32:1a:51:fb:91:77'), i
pv4(csum=9895,dst='10.0.0.2',flags=2,header_length=5,identification=0,offset=0
,option=None,proto=1,src='10.0.0.1',tos=0,total_length=84,ttl=64,version=4), i
cmp(code=0,csum=43748,data=echo(data='`\\xb9uS\\x00\\x00\\x00\\x00\\x7f\\'\\x0
1\\x00\\x00\\x00\\x00\\x00\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\
x1a\\x1b\\x1c\\x1d\\x1e\\x1f !\"#$%&\\'()*+,-./01234567',id=14355,seq=1),type=
8)"
Get arp table:
> {"jsonrpc": "2.0", "id": 1, "method": "get_arp_table", "params" : {}}
< {"jsonrpc": "2.0", "id": 1, "result": {"1": {"32:1a:51:fb:91:77": 1, "26:8c:
15:0c:de:49": 2}}}
"""
from os_ken.app import simple_switch_13
from os_ken.app.wsgi import ControllerBase
from os_ken.app.wsgi import rpc_public
from os_ken.app.wsgi import websocket
from os_ken.app.wsgi import WebSocketRPCServer
from os_ken.app.wsgi import WSGIApplication
from os_ken.controller import ofp_event
from os_ken.controller.handler import set_ev_cls
from os_ken.lib.packet import packet
simple_switch_instance_name = 'simple_switch_api_app'
url = '/simpleswitch/ws'
class SimpleSwitchWebSocket13(simple_switch_13.SimpleSwitch13):
_CONTEXTS = {
'wsgi': WSGIApplication,
}
def __init__(self, *args, **kwargs):
super(SimpleSwitchWebSocket13, self).__init__(*args, **kwargs)
wsgi = kwargs['wsgi']
wsgi.register(
SimpleSwitchWebSocketController,
data={simple_switch_instance_name: self},
)
self._ws_manager = wsgi.websocketmanager
@set_ev_cls(ofp_event.EventOFPPacketIn)
def _packet_in_handler(self, ev):
super(SimpleSwitchWebSocket13, self)._packet_in_handler(ev)
pkt = packet.Packet(ev.msg.data)
self._ws_manager.broadcast(str(pkt))
@rpc_public
def get_arp_table(self):
return self.mac_to_port
class SimpleSwitchWebSocketController(ControllerBase):
def __init__(self, req, link, data, **config):
super(SimpleSwitchWebSocketController, self).__init__(
req, link, data, **config)
self.simple_switch_app = data[simple_switch_instance_name]
@websocket('simpleswitch', url)
def _websocket_handler(self, ws):
simple_switch = self.simple_switch_app
simple_switch.logger.debug('WebSocket connected: %s', ws)
rpc_server = WebSocketRPCServer(ws, simple_switch)
rpc_server.serve_forever()
simple_switch.logger.debug('WebSocket disconnected: %s', ws)

View File

@ -1,120 +0,0 @@
# Copyright (C) 2014 Stratosphere Inc.
#
# 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.
"""
Usage example
1. Run this application:
$ osken-manager --verbose --observe-links os_ken.app.ws_topology
2. Connect to this application by WebSocket (use your favorite client):
$ wscat -c ws://localhost:8080/v1.0/topology/ws
3. Join switches (use your favorite method):
$ sudo mn --controller=remote --topo linear,2
4. Topology change is notified:
< {"params": [{"ports": [{"hw_addr": "56:c7:08:12:bb:36", "name": "s1-eth1", "port_no": "00000001", "dpid": "0000000000000001"}, {"hw_addr": "de:b9:49:24:74:3f", "name": "s1-eth2", "port_no": "00000002", "dpid": "0000000000000001"}], "dpid": "0000000000000001"}], "jsonrpc": "2.0", "method": "event_switch_enter", "id": 1}
> {"id": 1, "jsonrpc": "2.0", "result": ""}
< {"params": [{"ports": [{"hw_addr": "56:c7:08:12:bb:36", "name": "s1-eth1", "port_no": "00000001", "dpid": "0000000000000001"}, {"hw_addr": "de:b9:49:24:74:3f", "name": "s1-eth2", "port_no": "00000002", "dpid": "0000000000000001"}], "dpid": "0000000000000001"}], "jsonrpc": "2.0", "method": "event_switch_leave", "id": 2}
> {"id": 2, "jsonrpc": "2.0", "result": ""}
...
""" # noqa
from socket import error as SocketError
from tinyrpc.exc import InvalidReplyError
from os_ken.app.wsgi import (
ControllerBase,
WSGIApplication,
websocket,
WebSocketRPCClient
)
from os_ken.base import app_manager
from os_ken.topology import event, switches
from os_ken.controller.handler import set_ev_cls
class WebSocketTopology(app_manager.OSKenApp):
_CONTEXTS = {
'wsgi': WSGIApplication,
'switches': switches.Switches,
}
def __init__(self, *args, **kwargs):
super(WebSocketTopology, self).__init__(*args, **kwargs)
self.rpc_clients = []
wsgi = kwargs['wsgi']
wsgi.register(WebSocketTopologyController, {'app': self})
@set_ev_cls(event.EventSwitchEnter)
def _event_switch_enter_handler(self, ev):
msg = ev.switch.to_dict()
self._rpc_broadcall('event_switch_enter', msg)
@set_ev_cls(event.EventSwitchLeave)
def _event_switch_leave_handler(self, ev):
msg = ev.switch.to_dict()
self._rpc_broadcall('event_switch_leave', msg)
@set_ev_cls(event.EventLinkAdd)
def _event_link_add_handler(self, ev):
msg = ev.link.to_dict()
self._rpc_broadcall('event_link_add', msg)
@set_ev_cls(event.EventLinkDelete)
def _event_link_delete_handler(self, ev):
msg = ev.link.to_dict()
self._rpc_broadcall('event_link_delete', msg)
@set_ev_cls(event.EventHostAdd)
def _event_host_add_handler(self, ev):
msg = ev.host.to_dict()
self._rpc_broadcall('event_host_add', msg)
def _rpc_broadcall(self, func_name, msg):
disconnected_clients = []
for rpc_client in self.rpc_clients:
# NOTE: Although broadcasting is desired,
# RPCClient#get_proxy(one_way=True) does not work well
rpc_server = rpc_client.get_proxy()
try:
getattr(rpc_server, func_name)(msg)
except SocketError:
self.logger.debug('WebSocket disconnected: %s', rpc_client.ws)
disconnected_clients.append(rpc_client)
except InvalidReplyError as e:
self.logger.error(e)
for client in disconnected_clients:
self.rpc_clients.remove(client)
class WebSocketTopologyController(ControllerBase):
def __init__(self, req, link, data, **config):
super(WebSocketTopologyController, self).__init__(
req, link, data, **config)
self.app = data['app']
@websocket('topology', '/v1.0/topology/ws')
def _websocket_handler(self, ws):
rpc_client = WebSocketRPCClient(ws)
self.app.rpc_clients.append(rpc_client)
rpc_client.serve_forever()

View File

@ -1,336 +0,0 @@
# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2012 Isaku Yamahata <yamahata at private email ne jp>
#
# 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 inspect
from types import MethodType
from routes import Mapper
from routes.util import URLGenerator
import six
from tinyrpc.server import RPCServer
from tinyrpc.dispatch import RPCDispatcher
from tinyrpc.dispatch import public as rpc_public
from tinyrpc.protocols.jsonrpc import JSONRPCProtocol
from tinyrpc.transports import ServerTransport, ClientTransport
from tinyrpc.client import RPCClient
import webob.dec
import webob.exc
from webob.request import Request as webob_Request
from webob.response import Response as webob_Response
from os_ken import cfg
from os_ken.lib import hub
DEFAULT_WSGI_HOST = '0.0.0.0'
DEFAULT_WSGI_PORT = 8080
CONF = cfg.CONF
CONF.register_cli_opts([
cfg.StrOpt(
'wsapi-host', default=DEFAULT_WSGI_HOST,
help='webapp listen host (default %s)' % DEFAULT_WSGI_HOST),
cfg.IntOpt(
'wsapi-port', default=DEFAULT_WSGI_PORT,
help='webapp listen port (default %s)' % DEFAULT_WSGI_PORT),
])
HEX_PATTERN = r'0x[0-9a-z]+'
DIGIT_PATTERN = r'[1-9][0-9]*'
def route(name, path, methods=None, requirements=None):
def _route(controller_method):
controller_method.routing_info = {
'name': name,
'path': path,
'methods': methods,
'requirements': requirements,
}
return controller_method
return _route
class Request(webob_Request):
"""
Wrapper class for webob.request.Request.
The behavior of this class is the same as webob.request.Request
except for setting "charset" to "UTF-8" automatically.
"""
DEFAULT_CHARSET = "UTF-8"
def __init__(self, environ, charset=DEFAULT_CHARSET, *args, **kwargs):
super(Request, self).__init__(
environ, charset=charset, *args, **kwargs)
class Response(webob_Response):
"""
Wrapper class for webob.response.Response.
The behavior of this class is the same as webob.response.Response
except for setting "charset" to "UTF-8" automatically.
"""
DEFAULT_CHARSET = "UTF-8"
def __init__(self, charset=DEFAULT_CHARSET, *args, **kwargs):
super(Response, self).__init__(charset=charset, *args, **kwargs)
class WebSocketRegistrationWrapper(object):
def __init__(self, func, controller):
self._controller = controller
self._controller_method = MethodType(func, controller)
def __call__(self, ws):
wsgi_application = self._controller.parent
ws_manager = wsgi_application.websocketmanager
ws_manager.add_connection(ws)
try:
self._controller_method(ws)
finally:
ws_manager.delete_connection(ws)
class _AlreadyHandledResponse(Response):
# XXX: Eventlet API should not be used directly.
from eventlet.wsgi import ALREADY_HANDLED
_ALREADY_HANDLED = ALREADY_HANDLED
def __call__(self, environ, start_response):
return self._ALREADY_HANDLED
def websocket(name, path):
def _websocket(controller_func):
def __websocket(self, req, **_):
wrapper = WebSocketRegistrationWrapper(controller_func, self)
ws_wsgi = hub.WebSocketWSGI(wrapper)
ws_wsgi(req.environ, req.start_response)
# XXX: In order to prevent the writing to a already closed socket.
# This issue is caused by combined use:
# - webob.dec.wsgify()
# - eventlet.wsgi.HttpProtocol.handle_one_response()
return _AlreadyHandledResponse()
__websocket.routing_info = {
'name': name,
'path': path,
'methods': None,
'requirements': None,
}
return __websocket
return _websocket
class ControllerBase(object):
special_vars = ['action', 'controller']
def __init__(self, req, link, data, **config):
self.req = req
self.link = link
self.data = data
self.parent = None
for name, value in config.items():
setattr(self, name, value)
def __call__(self, req):
action = self.req.urlvars.get('action', 'index')
if hasattr(self, '__before__'):
self.__before__()
kwargs = self.req.urlvars.copy()
for attr in self.special_vars:
if attr in kwargs:
del kwargs[attr]
return getattr(self, action)(req, **kwargs)
class WebSocketDisconnectedError(Exception):
pass
class WebSocketServerTransport(ServerTransport):
def __init__(self, ws):
self.ws = ws
def receive_message(self):
message = self.ws.wait()
if message is None:
raise WebSocketDisconnectedError()
context = None
return context, message
def send_reply(self, context, reply):
self.ws.send(six.text_type(reply))
class WebSocketRPCServer(RPCServer):
def __init__(self, ws, rpc_callback):
dispatcher = RPCDispatcher()
dispatcher.register_instance(rpc_callback)
super(WebSocketRPCServer, self).__init__(
WebSocketServerTransport(ws),
JSONRPCProtocol(),
dispatcher,
)
def serve_forever(self):
try:
super(WebSocketRPCServer, self).serve_forever()
except WebSocketDisconnectedError:
return
def _spawn(self, func, *args, **kwargs):
hub.spawn(func, *args, **kwargs)
class WebSocketClientTransport(ClientTransport):
def __init__(self, ws, queue):
self.ws = ws
self.queue = queue
def send_message(self, message, expect_reply=True):
self.ws.send(six.text_type(message))
if expect_reply:
return self.queue.get()
class WebSocketRPCClient(RPCClient):
def __init__(self, ws):
self.ws = ws
self.queue = hub.Queue()
super(WebSocketRPCClient, self).__init__(
JSONRPCProtocol(),
WebSocketClientTransport(ws, self.queue),
)
def serve_forever(self):
while True:
msg = self.ws.wait()
if msg is None:
break
self.queue.put(msg)
class wsgify_hack(webob.dec.wsgify):
def __call__(self, environ, start_response):
self.kwargs['start_response'] = start_response
return super(wsgify_hack, self).__call__(environ, start_response)
class WebSocketManager(object):
def __init__(self):
self._connections = []
def add_connection(self, ws):
self._connections.append(ws)
def delete_connection(self, ws):
self._connections.remove(ws)
def broadcast(self, msg):
for connection in self._connections:
connection.send(msg)
class WSGIApplication(object):
def __init__(self, **config):
self.config = config
self.mapper = Mapper()
self.registory = {}
self._wsmanager = WebSocketManager()
super(WSGIApplication, self).__init__()
def _match(self, req):
# Note: Invoke the new API, first. If the arguments unmatched,
# invoke the old API.
try:
return self.mapper.match(environ=req.environ)
except TypeError:
self.mapper.environ = req.environ
return self.mapper.match(req.path_info)
@wsgify_hack
def __call__(self, req, start_response):
match = self._match(req)
if not match:
return webob.exc.HTTPNotFound()
req.start_response = start_response
req.urlvars = match
link = URLGenerator(self.mapper, req.environ)
data = None
name = match['controller'].__name__
if name in self.registory:
data = self.registory[name]
controller = match['controller'](req, link, data, **self.config)
controller.parent = self
return controller(req)
def register(self, controller, data=None):
def _target_filter(attr):
if not inspect.ismethod(attr) and not inspect.isfunction(attr):
return False
if not hasattr(attr, 'routing_info'):
return False
return True
methods = inspect.getmembers(controller, _target_filter)
for method_name, method in methods:
routing_info = getattr(method, 'routing_info')
name = routing_info['name']
path = routing_info['path']
conditions = {}
if routing_info.get('methods'):
conditions['method'] = routing_info['methods']
requirements = routing_info.get('requirements') or {}
self.mapper.connect(name,
path,
controller=controller,
requirements=requirements,
action=method_name,
conditions=conditions)
if data:
self.registory[controller.__name__] = data
@property
def websocketmanager(self):
return self._wsmanager
class WSGIServer(hub.WSGIServer):
def __init__(self, application, **config):
super(WSGIServer, self).__init__((CONF.wsapi_host, CONF.wsapi_port),
application, **config)
def __call__(self):
self.serve_forever()
def start_service(app_mgr):
for instance in app_mgr.contexts.values():
if instance.__class__ == WSGIApplication:
return WSGIServer(instance)
return None

View File

@ -32,7 +32,6 @@ import gc
from os_ken import cfg
from os_ken import utils
from os_ken.app import wsgi
from os_ken.controller.handler import register_instance, get_dependent_services
from os_ken.controller.controller import Datapath
from os_ken.controller import event
@ -363,9 +362,6 @@ class AppManager(object):
app_mgr.load_apps(app_lists)
contexts = app_mgr.create_contexts()
services = app_mgr.instantiate_apps(**contexts)
webapp = wsgi.start_service(app_mgr)
if webapp:
services.append(hub.spawn(webapp))
try:
hub.joinall(services)
finally:

View File

@ -30,7 +30,6 @@ log.early_init_log(logging.DEBUG)
from os_ken import flags
from os_ken import __version__ as version
from os_ken.app import wsgi
from os_ken.base.app_manager import AppManager
from os_ken.controller import controller
from os_ken.topology import switches
@ -100,11 +99,6 @@ def main(args=None, prog=None):
services = []
services.extend(app_mgr.instantiate_apps(**contexts))
webapp = wsgi.start_service(app_mgr)
if webapp:
thr = hub.spawn(webapp)
services.append(thr)
try:
hub.joinall(services)
except KeyboardInterrupt:

View File

@ -1,92 +0,0 @@
# Copyright (C) 2014 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.
from os_ken.base import app_manager
from os_ken.lib import hub
from os_ken.app.wsgi import websocket, ControllerBase, WSGIApplication
from os_ken.app.wsgi import rpc_public, WebSocketRPCServer
from os_ken.services.protocols.bgp.api.base import call
from os_ken.services.protocols.bgp.api.base import PREFIX
from os_ken.services.protocols.bgp.rtconf.common import LOCAL_AS
from os_ken.services.protocols.bgp.rtconf.common import ROUTER_ID
from os_ken.services.protocols.bgp.rtconf import neighbors
bgp_instance_name = 'bgp_api_app'
url = '/bgp/ws'
class BgpWSJsonRpc(app_manager.OSKenApp):
_CONTEXTS = {
'wsgi': WSGIApplication,
}
def __init__(self, *args, **kwargs):
super(BgpWSJsonRpc, self).__init__(*args, **kwargs)
wsgi = kwargs['wsgi']
wsgi.register(
BgpWSJsonRpcController,
data={bgp_instance_name: self},
)
self._ws_manager = wsgi.websocketmanager
@rpc_public('core.start')
def _core_start(self, as_number=64512, router_id='10.0.0.1'):
common_settings = {}
common_settings[LOCAL_AS] = as_number
common_settings[ROUTER_ID] = str(router_id)
waiter = hub.Event()
call('core.start', waiter=waiter, **common_settings)
waiter.wait()
return {}
@rpc_public('neighbor.create')
def _neighbor_create(self, ip_address='192.168.177.32',
remote_as=64513):
bgp_neighbor = {}
bgp_neighbor[neighbors.IP_ADDRESS] = str(ip_address)
bgp_neighbor[neighbors.REMOTE_AS] = remote_as
call('neighbor.create', **bgp_neighbor)
return {}
@rpc_public('network.add')
def _prefix_add(self, prefix='10.20.0.0/24'):
networks = {}
networks[PREFIX] = str(prefix)
call('network.add', **networks)
return {}
@rpc_public('neighbors.get')
def _neighbors_get(self):
return call('neighbors.get')
@rpc_public('show.rib')
def _show_rib(self, family='ipv4'):
show = {}
show['params'] = ['rib', family]
return call('operator.show', **show)
class BgpWSJsonRpcController(ControllerBase):
def __init__(self, req, link, data, **config):
super(BgpWSJsonRpcController, self).__init__(
req, link, data, **config)
self.bgp_api_app = data[bgp_instance_name]
@websocket('bgp', url)
def _websocket_handler(self, ws):
rpc_server = WebSocketRPCServer(ws, self.bgp_api_app)
rpc_server.serve_forever()

View File

@ -1,101 +0,0 @@
[
{
"method": "GET",
"path": "/stats/switches"
},
{
"method": "GET",
"path": "/stats/desc/1"
},
{
"method": "GET",
"path": "/stats/flow/1"
},
{
"method": "POST",
"path": "/stats/flow/1"
},
{
"method": "GET",
"path": "/stats/aggregateflow/1"
},
{
"method": "POST",
"path": "/stats/aggregateflow/1"
},
{
"method": "GET",
"path": "/stats/port/1"
},
{
"method": "GET",
"path": "/stats/port/1/1"
},
{
"method": "GET",
"path": "/stats/portdesc/1"
},
{
"method": "GET",
"path": "/stats/queue/1"
},
{
"method": "GET",
"path": "/stats/queue/1/1"
},
{
"method": "GET",
"path": "/stats/queue/1/1/1"
},
{
"method": "GET",
"path": "/stats/table/1"
},
{
"method": "POST",
"path": "/stats/flowentry/add",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/modify",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/modify_strict",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/delete",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/delete_strict",
"body": {
"dpid": 1
}
},
{
"method": "DELETE",
"path": "/stats/flowentry/clear/1"
},
{
"method": "POST",
"path": "/stats/portdesc/modify",
"body": {
"dpid": 1,
"port_no": 1
}
}
]

View File

@ -1,150 +0,0 @@
[
{
"method": "GET",
"path": "/stats/switches"
},
{
"method": "GET",
"path": "/stats/desc/1"
},
{
"method": "GET",
"path": "/stats/flow/1"
},
{
"method": "POST",
"path": "/stats/flow/1"
},
{
"method": "GET",
"path": "/stats/aggregateflow/1"
},
{
"method": "POST",
"path": "/stats/aggregateflow/1"
},
{
"method": "GET",
"path": "/stats/port/1"
},
{
"method": "GET",
"path": "/stats/port/1/1"
},
{
"method": "GET",
"path": "/stats/portdesc/1"
},
{
"method": "GET",
"path": "/stats/queue/1"
},
{
"method": "GET",
"path": "/stats/queue/1/1"
},
{
"method": "GET",
"path": "/stats/queue/1/1/1"
},
{
"method": "GET",
"path": "/stats/queueconfig/1"
},
{
"method": "GET",
"path": "/stats/queueconfig/1/1"
},
{
"method": "GET",
"path": "/stats/group/1"
},
{
"method": "GET",
"path": "/stats/group/1/1"
},
{
"method": "GET",
"path": "/stats/groupdesc/1"
},
{
"method": "GET",
"path": "/stats/groupfeatures/1"
},
{
"method": "GET",
"path": "/stats/table/1"
},
{
"method": "POST",
"path": "/stats/flowentry/add",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/modify",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/modify_strict",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/delete",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/delete_strict",
"body": {
"dpid": 1
}
},
{
"method": "DELETE",
"path": "/stats/flowentry/clear/1"
},
{
"method": "POST",
"path": "/stats/groupentry/add",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/groupentry/modify",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/groupentry/delete",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/portdesc/modify",
"body": {
"dpid": 1,
"port_no": 1
}
},
{
"method": "POST",
"path": "/stats/experimenter/1"
}
]

View File

@ -1,191 +0,0 @@
[
{
"method": "GET",
"path": "/stats/switches"
},
{
"method": "GET",
"path": "/stats/desc/1"
},
{
"method": "GET",
"path": "/stats/flow/1"
},
{
"method": "POST",
"path": "/stats/flow/1"
},
{
"method": "GET",
"path": "/stats/aggregateflow/1"
},
{
"method": "POST",
"path": "/stats/aggregateflow/1"
},
{
"method": "GET",
"path": "/stats/port/1"
},
{
"method": "GET",
"path": "/stats/port/1/1"
},
{
"method": "GET",
"path": "/stats/portdesc/1"
},
{
"method": "GET",
"path": "/stats/queue/1"
},
{
"method": "GET",
"path": "/stats/queue/1/1"
},
{
"method": "GET",
"path": "/stats/queue/1/1/1"
},
{
"method": "GET",
"path": "/stats/queueconfig/1"
},
{
"method": "GET",
"path": "/stats/queueconfig/1/1"
},
{
"method": "GET",
"path": "/stats/group/1"
},
{
"method": "GET",
"path": "/stats/group/1/1"
},
{
"method": "GET",
"path": "/stats/groupdesc/1"
},
{
"method": "GET",
"path": "/stats/groupfeatures/1"
},
{
"method": "GET",
"path": "/stats/meter/1"
},
{
"method": "GET",
"path": "/stats/meter/1/1"
},
{
"method": "GET",
"path": "/stats/meterconfig/1"
},
{
"method": "GET",
"path": "/stats/meterconfig/1/1"
},
{
"method": "GET",
"path": "/stats/meterfeatures/1"
},
{
"method": "GET",
"path": "/stats/table/1"
},
{
"method": "POST",
"path": "/stats/flowentry/add",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/modify",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/modify_strict",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/delete",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/delete_strict",
"body": {
"dpid": 1
}
},
{
"method": "DELETE",
"path": "/stats/flowentry/clear/1"
},
{
"method": "POST",
"path": "/stats/groupentry/add",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/groupentry/modify",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/groupentry/delete",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/portdesc/modify",
"body": {
"dpid": 1,
"port_no": 1
}
},
{
"method": "POST",
"path": "/stats/meterentry/add",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/meterentry/modify",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/meterentry/delete",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/experimenter/1"
}
]

View File

@ -1,195 +0,0 @@
[
{
"method": "GET",
"path": "/stats/switches"
},
{
"method": "GET",
"path": "/stats/desc/1"
},
{
"method": "GET",
"path": "/stats/flow/1"
},
{
"method": "POST",
"path": "/stats/flow/1"
},
{
"method": "GET",
"path": "/stats/aggregateflow/1"
},
{
"method": "POST",
"path": "/stats/aggregateflow/1"
},
{
"method": "GET",
"path": "/stats/port/1"
},
{
"method": "GET",
"path": "/stats/port/1/1"
},
{
"method": "GET",
"path": "/stats/portdesc/1"
},
{
"method": "GET",
"path": "/stats/queue/1"
},
{
"method": "GET",
"path": "/stats/queue/1/1"
},
{
"method": "GET",
"path": "/stats/queue/1/1/1"
},
{
"method": "GET",
"path": "/stats/queuedesc/1"
},
{
"method": "GET",
"path": "/stats/queuedesc/1/1"
},
{
"method": "GET",
"path": "/stats/queuedesc/1/1/1"
},
{
"method": "GET",
"path": "/stats/group/1"
},
{
"method": "GET",
"path": "/stats/group/1/1"
},
{
"method": "GET",
"path": "/stats/groupdesc/1"
},
{
"method": "GET",
"path": "/stats/groupfeatures/1"
},
{
"method": "GET",
"path": "/stats/meter/1"
},
{
"method": "GET",
"path": "/stats/meter/1/1"
},
{
"method": "GET",
"path": "/stats/meterconfig/1"
},
{
"method": "GET",
"path": "/stats/meterconfig/1/1"
},
{
"method": "GET",
"path": "/stats/meterfeatures/1"
},
{
"method": "GET",
"path": "/stats/table/1"
},
{
"method": "POST",
"path": "/stats/flowentry/add",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/modify",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/modify_strict",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/delete",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/delete_strict",
"body": {
"dpid": 1
}
},
{
"method": "DELETE",
"path": "/stats/flowentry/clear/1"
},
{
"method": "POST",
"path": "/stats/groupentry/add",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/groupentry/modify",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/groupentry/delete",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/portdesc/modify",
"body": {
"dpid": 1,
"port_no": 1
}
},
{
"method": "POST",
"path": "/stats/meterentry/add",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/meterentry/modify",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/meterentry/delete",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/experimenter/1"
}
]

View File

@ -1,203 +0,0 @@
[
{
"method": "GET",
"path": "/stats/switches"
},
{
"method": "GET",
"path": "/stats/desc/1"
},
{
"method": "GET",
"path": "/stats/flow/1"
},
{
"method": "POST",
"path": "/stats/flow/1"
},
{
"method": "GET",
"path": "/stats/aggregateflow/1"
},
{
"method": "POST",
"path": "/stats/aggregateflow/1"
},
{
"method": "GET",
"path": "/stats/port/1"
},
{
"method": "GET",
"path": "/stats/port/1/1"
},
{
"method": "GET",
"path": "/stats/portdesc/1"
},
{
"method": "GET",
"path": "/stats/portdesc/1/1"
},
{
"method": "GET",
"path": "/stats/queue/1"
},
{
"method": "GET",
"path": "/stats/queue/1/1"
},
{
"method": "GET",
"path": "/stats/queue/1/1/1"
},
{
"method": "GET",
"path": "/stats/queuedesc/1"
},
{
"method": "GET",
"path": "/stats/queuedesc/1/1"
},
{
"method": "GET",
"path": "/stats/queuedesc/1/1/1"
},
{
"method": "GET",
"path": "/stats/group/1"
},
{
"method": "GET",
"path": "/stats/group/1/1"
},
{
"method": "GET",
"path": "/stats/groupdesc/1"
},
{
"method": "GET",
"path": "/stats/groupdesc/1/1"
},
{
"method": "GET",
"path": "/stats/groupfeatures/1"
},
{
"method": "GET",
"path": "/stats/meter/1"
},
{
"method": "GET",
"path": "/stats/meter/1/1"
},
{
"method": "GET",
"path": "/stats/meterdesc/1"
},
{
"method": "GET",
"path": "/stats/meterdesc/1/1"
},
{
"method": "GET",
"path": "/stats/meterfeatures/1"
},
{
"method": "GET",
"path": "/stats/table/1"
},
{
"method": "POST",
"path": "/stats/flowentry/add",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/modify",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/modify_strict",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/delete",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/flowentry/delete_strict",
"body": {
"dpid": 1
}
},
{
"method": "DELETE",
"path": "/stats/flowentry/clear/1"
},
{
"method": "POST",
"path": "/stats/groupentry/add",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/groupentry/modify",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/groupentry/delete",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/portdesc/modify",
"body": {
"dpid": 1,
"port_no": 1
}
},
{
"method": "POST",
"path": "/stats/meterentry/add",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/meterentry/modify",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/meterentry/delete",
"body": {
"dpid": 1
}
},
{
"method": "POST",
"path": "/stats/experimenter/1"
}
]

View File

@ -1,136 +0,0 @@
# Copyright (C) 2016 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.
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import functools
import json
import logging
import os
import sys
import unittest
from unittest import mock
from nose.tools import eq_
from os_ken.app import ofctl_rest
from os_ken.app.wsgi import Request
from os_ken.app.wsgi import WSGIApplication
from os_ken.controller.dpset import DPSet
from os_ken.ofproto import ofproto_protocol
from os_ken.ofproto import ofproto_v1_0
from os_ken.ofproto import ofproto_v1_2
from os_ken.ofproto import ofproto_v1_3
from os_ken.ofproto import ofproto_v1_4
from os_ken.ofproto import ofproto_v1_5
from os_ken.tests import test_lib
LOG = logging.getLogger(__name__)
class DummyDatapath(ofproto_protocol.ProtocolDesc):
def __init__(self, version):
super(DummyDatapath, self).__init__(version)
self.id = 1
_kw = {'port_no': 1, 'hw_addr': 'aa:bb:cc:dd:ee:ff',
'name': 's1-eth1', 'config': 1, 'state': 1}
# for OpenFlow 1.0
if version in [ofproto_v1_0.OFP_VERSION]:
_kw.update(
{'curr': 2112, 'advertised': 0, 'supported': 0, 'peer': 0})
port_info = self.ofproto_parser.OFPPhyPort(**_kw)
# for OpenFlow 1.2 or 1.3
elif version in [ofproto_v1_2.OFP_VERSION, ofproto_v1_3.OFP_VERSION]:
_kw.update(
{'curr': 2112, 'advertised': 0, 'supported': 0, 'peer': 0,
'curr_speed': 10000000, 'max_speed': 0})
port_info = self.ofproto_parser.OFPPort(**_kw)
# for OpenFlow 1.4+
else:
_kw.update({'properties': []})
port_info = self.ofproto_parser.OFPPort(**_kw)
self.ports = {1: port_info}
class Test_ofctl_rest(unittest.TestCase):
def _test(self, name, dp, method, path, body):
# print('processing %s ...' % name)
dpset = DPSet()
dpset._register(dp)
wsgi = WSGIApplication()
contexts = {
'dpset': dpset,
'wsgi': wsgi,
}
ofctl_rest.RestStatsApi(**contexts)
req = Request.blank(path)
req.body = json.dumps(body).encode('utf-8')
req.method = method
with mock.patch('os_ken.lib.ofctl_utils.send_stats_request'),\
mock.patch('os_ken.lib.ofctl_utils.send_msg'):
res = req.get_response(wsgi)
eq_(res.status, '200 OK')
def _add_tests():
_ofp_vers = {
'of10': ofproto_v1_0.OFP_VERSION,
'of12': ofproto_v1_2.OFP_VERSION,
'of13': ofproto_v1_3.OFP_VERSION,
'of14': ofproto_v1_4.OFP_VERSION,
'of15': ofproto_v1_5.OFP_VERSION,
}
this_dir = os.path.dirname(sys.modules[__name__].__file__)
ofctl_rest_json_dir = os.path.join(this_dir, 'ofctl_rest_json/')
for ofp_ver in _ofp_vers:
# read a json file
json_path = os.path.join(ofctl_rest_json_dir, ofp_ver + '.json')
if os.path.exists(json_path):
_test_cases = json.load(open(json_path))
else:
# print("Skip to load test cases for %s" % ofp_ver)
continue
# add test
for test in _test_cases:
method = test['method']
path = test['path']
body = test.get('body', {})
name = 'test_ofctl_rest_' + method + '_' + ofp_ver + '_' + path
# print('adding %s ...' % name)
f = functools.partial(
Test_ofctl_rest._test,
name=name,
dp=DummyDatapath(_ofp_vers[ofp_ver]),
method=test['method'],
path=test['path'],
body=body
)
test_lib.add_method(Test_ofctl_rest, name, f)
_add_tests()
if __name__ == "__main__":
unittest.main()

View File

@ -1,464 +0,0 @@
# Copyright (C) 2014 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.
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import unittest
from nose.tools import *
import binascii
import inspect
import json
import logging
import math
import netaddr
import os
import signal
import sys
import time
import traceback
from random import randint
from os_ken import cfg
# import all packet libraries.
PKT_LIB_PATH = 'os_ken.lib.packet'
for modname, moddef in sys.modules.items():
if not modname.startswith(PKT_LIB_PATH) or not moddef:
continue
for (clsname, clsdef, ) in inspect.getmembers(moddef):
if not inspect.isclass(clsdef):
continue
exec('from %s import %s' % (modname, clsname))
from os_ken.base import app_manager
from os_ken.controller import handler
from os_ken.controller import ofp_event
from os_ken.controller.handler import set_ev_cls
from os_ken.exception import OSKenException
from os_ken.lib import dpid as dpid_lib
from os_ken.lib import hub
from os_ken.lib import stringify
from os_ken.lib.packet import packet
from os_ken.ofproto import ofproto_protocol
from os_ken.ofproto import ofproto_v1_3
from os_ken.ofproto import ofproto_v1_3_parser
from os_ken.ofproto import ofproto_v1_4
from os_ken.tests.switch.tester import TestPatterns
from os_ken.tests.switch.tester import TestFile
from os_ken.tests.switch.tester import OfTester
CONF = cfg.CONF
LOG = logging.getLogger('test_tester')
SAMPLE_DESC = "action: 00_OUTPUT"
class Test_tester(unittest.TestCase):
""" Test case for tester
"""
# action/00_OUTPUT.json
test_json_1 = {
"description": "ethernet/ipv4/tcp-->'actions=output:2'",
"prerequisite": [
{
"OFPFlowMod": {
"table_id": 0,
"instructions": [
{
"OFPInstructionActions": {
"actions": [
{
"OFPActionOutput": {
"port": "target_send_port_1"
}
}
],
"type": 4
}
}
]
}
}
],
"tests": [
{
"ingress": [
"ethernet(dst='22:22:22:22:22:22', \
src='12:11:11:11:11:11', ethertype=2048)",
"ipv4(tos=32, proto=6, src='192.168.10.10', \
dst='192.168.20.20', ttl=64)",
"tcp(dst_port=2222, option=str('\\x00' * 4), \
src_port=11111)",
"'\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x0\
8\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x1\
2\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1\
b\\x1c\\x1d\\x1e\\x1f'"
],
"egress":[
"ethernet(dst='22:22:22:22:22:22', \
src='12:11:11:11:11:11', ethertype=2048)",
"ipv4(tos=32, proto=6, src='192.168.10.10', \
dst='192.168.20.20', ttl=64)",
"tcp(dst_port=2222, option=str('\\x00' * 4), \
src_port=11111)",
"'\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x0\
8\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x1\
2\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1\
b\\x1c\\x1d\\x1e\\x1f'"
]
}
]
}
# group/00_ALL.json
test_json_2 = {
"description": "2Mbps(ethernet/ipv4/tcp)-->'in_port=1,\
actions=group:all(actions=output:2/actions=output:3)'",
"prerequisite": [
{
"OFPGroupMod": {
"group_id": 0,
"buckets": [
{
"OFPBucket": {
"actions": [
{
"OFPActionOutput": {
"port": "target_send_port_1"
}
}
]
}
},
{
"OFPBucket": {
"actions": [
{
"OFPActionOutput": {
"port": "target_send_port_2"
}
}
]
}
}
]
}
},
{
"OFPFlowMod": {
"match": {
"OFPMatch": {
"oxm_fields": [
{
"OXMTlv": {
"field": "in_port",
"value": "target_recv_port"
}
}
]
}
},
"instructions": [
{
"OFPInstructionActions": {
"actions": [
{
"OFPActionGroup": {
"group_id": 0
}
}
],
"type": 4
}
}
]
}
}
],
"tests": [
{
"ingress": {
"packets": {
"data": [
"ethernet(dst='22:22:22:22:22:22', \
src='12:11:11:11:11:11', ethertype=2048)",
"ipv4(proto=6)",
"tcp()",
"str('\\x11' * (1500 - 54))"
],
"pktps":175,
"duration_time":30
}
},
"egress":{
"throughput": [
{
"OFPMatch": {
"oxm_fields": [
{
"OXMTlv": {
"field": "in_port",
"value": "tester_recv_port_1"
}
}
]
},
"kbps": 2000
},
{
"OFPMatch": {
"oxm_fields": [
{
"OXMTlv": {
"field": "in_port",
"value": "tester_recv_port_2"
}
}
]
},
"kbps": 2000
}
]
}
}
]
}
# match/00_IN_PORT.json
test_json_3 = {
"description": "ethernet/ipv4/tcp-->'in_port=1,actions=output:2'",
"prerequisite": [
{
"OFPFlowMod": {
"table_id": 0,
"match": {
"OFPMatch": {
"oxm_fields": [
{
"OXMTlv": {
"field": "in_port",
"value": "target_recv_port"
}
}
]
}
},
"instructions": [
{
"OFPInstructionActions": {
"actions": [
{
"OFPActionOutput": {
"port": "target_send_port_1"
}
}
],
"type": 4
}
}
]
}
}
],
"tests": [
{
"ingress": [
"ethernet(dst='22:22:22:22:22:22', \
src='12:11:11:11:11:11', ethertype=2048)",
"ipv4(tos=32, proto=6, src='192.168.10.10', \
dst='192.168.20.20', ttl=64)",
"tcp(dst_port=2222, option=str('\\x00' * 4), \
src_port=11111)",
"'\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x0\
8\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x1\
2\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1\
b\\x1c\\x1d\\x1e\\x1f'"
],
"egress":[
"ethernet(dst='22:22:22:22:22:22', \
src='12:11:11:11:11:11', ethertype=2048)",
"ipv4(tos=32, proto=6, src='192.168.10.10',\
dst='192.168.20.20', ttl=64)",
"tcp(dst_port=2222, option=str('\\x00' * 4), \
src_port=11111)",
"'\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x0\
8\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x1\
2\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1\
b\\x1c\\x1d\\x1e\\x1f'"
]
}
]
}
# meter/01_DROP_00_KBPS_00_1M.json
test_json_4 = {
"description": "2Mbps(ethernet/ipv4/tcp)-->'in_port=1,\
actions=meter:1Mbps(drop),output:2'",
"prerequisite": [
{
"OFPMeterMod": {
"meter_id": 1,
"bands": [
{
"OFPMeterBandDrop": {
"rate": 1000
}
}
]
}
},
{
"OFPFlowMod": {
"match": {
"OFPMatch": {
"oxm_fields": [
{
"OXMTlv": {
"field": "in_port",
"value": "target_recv_port"
}
}
]
}
},
"instructions": [
{
"OFPInstructionMeter": {
"meter_id": 1
}
},
{
"OFPInstructionActions": {
"actions": [
{
"OFPActionOutput": {
"port": "target_send_port_1"
}
}
],
"type": 4
}
}
]
}
}
],
"tests": [
{
"ingress": {
"packets": {
"data": [
"ethernet(dst='22:22:22:22:22:22', \
src='12:11:11:11:11:11', ethertype=2048)",
"ipv4(proto=6)",
"tcp()",
"str('\\x11' * (1500 - 54))"
],
"pktps":175,
"duration_time":30
}
},
"egress":{
"throughput": [
{
"OFPMatch": {
"oxm_fields": [
{
"OXMTlv": {
"field": "in_port",
"value": "tester_recv_port_1"
}
}
]
},
"kbps": 1000
}
]
}
}
]
}
def setUp(self):
OfTester.tester_ver = ofproto_v1_3.OFP_VERSION
OfTester.target_ver = ofproto_v1_3.OFP_VERSION
def tearDown(self):
pass
def test__normalize_test_json(self):
self.tests = TestPatterns(
"../switch/of13/action/00_OUTPUT.json",
logging.getLogger("test_tester"))
self.tests[SAMPLE_DESC]._normalize_test_json(Test_tester.test_json_1)
self.tests[SAMPLE_DESC]._normalize_test_json(Test_tester.test_json_2)
self.tests[SAMPLE_DESC]._normalize_test_json(Test_tester.test_json_3)
self.tests[SAMPLE_DESC]._normalize_test_json(Test_tester.test_json_4)
# action/00_OUTPUT.json
eq_(Test_tester.test_json_1["prerequisite"][0]["OFPFlowMod"][
"instructions"][0]["OFPInstructionActions"][
"actions"][0]["OFPActionOutput"]["port"],
CONF['test-switch']['target_send_port_1'])
# group/00_ALL.json
eq_(Test_tester.test_json_2["prerequisite"][1]["OFPFlowMod"][
"match"]["OFPMatch"]["oxm_fields"][0]["OXMTlv"]["value"],
CONF['test-switch']['target_recv_port'])
eq_(Test_tester.test_json_2["prerequisite"][0]["OFPGroupMod"][
"buckets"][0]["OFPBucket"]["actions"][0]["OFPActionOutput"][
"port"], CONF['test-switch']['target_send_port_1'])
eq_(Test_tester.test_json_2["prerequisite"][0]["OFPGroupMod"][
"buckets"][1]["OFPBucket"]["actions"][0]["OFPActionOutput"][
"port"], CONF['test-switch']['target_send_port_2'])
eq_(Test_tester.test_json_2["tests"][0]["egress"]["throughput"][
0]["OFPMatch"]["oxm_fields"][0]["OXMTlv"]["value"],
CONF['test-switch']['tester_recv_port_1'])
eq_(Test_tester.test_json_2["tests"][0]["egress"]["throughput"][
1]["OFPMatch"]["oxm_fields"][0]["OXMTlv"]["value"],
CONF['test-switch']['tester_recv_port_2'])
# match/00_IN_PORT.json
eq_(Test_tester.test_json_3["prerequisite"][0]["OFPFlowMod"][
"match"]["OFPMatch"]["oxm_fields"][0]["OXMTlv"]["value"],
CONF['test-switch']['target_recv_port'])
eq_(Test_tester.test_json_3["prerequisite"][0]["OFPFlowMod"][
"instructions"][0]["OFPInstructionActions"]["actions"][0][
"OFPActionOutput"]["port"], CONF['test-switch'][
'target_send_port_1'])
# meter/01_DROP_00_KBPS_00_1M.json
eq_(Test_tester.test_json_4["prerequisite"][1]["OFPFlowMod"][
"match"]["OFPMatch"]["oxm_fields"][0]["OXMTlv"]["value"],
CONF['test-switch']['target_recv_port'])
eq_(Test_tester.test_json_4["prerequisite"][1]["OFPFlowMod"][
"instructions"][1]["OFPInstructionActions"]["actions"][0][
"OFPActionOutput"]["port"],
CONF['test-switch']['target_send_port_1'])
eq_(Test_tester.test_json_4["tests"][0]["egress"]["throughput"][
0]["OFPMatch"]["oxm_fields"][0]["OXMTlv"]["value"],
CONF['test-switch']['tester_recv_port_1'])

View File

@ -1,55 +0,0 @@
# Copyright (C) 2013 Stratosphere Inc.
#
# 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.
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import unittest
from unittest import mock
from socket import error as SocketError
from os_ken.app.ws_topology import WebSocketTopology
class Test_ws_topology(unittest.TestCase):
def test_when_sock_error(self):
args = {
'wsgi': mock.Mock(),
}
app = WebSocketTopology(**args)
rpc_client_mock1 = mock.Mock()
config = {
'get_proxy.return_value.event_link_add.side_effect': SocketError,
}
rpc_client_mock1.configure_mock(**config)
rpc_client_mock2 = mock.Mock()
app.rpc_clients = [
rpc_client_mock1,
rpc_client_mock2,
]
ev_mock = mock.Mock()
app._event_link_add_handler(ev_mock)
rpc_client_mock1.get_proxy.assert_called_once_with()
rpc_client_mock2.get_proxy.assert_called_once_with()
if __name__ == "__main__":
unittest.main()

View File

@ -1,104 +0,0 @@
# Copyright (C) 2013 Stratosphere Inc.
#
# 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.
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import unittest
import logging
import nose
from nose.tools import eq_
from os_ken.app.wsgi import ControllerBase
from os_ken.app.wsgi import WSGIApplication
from os_ken.app.wsgi import Response
from os_ken.app.wsgi import route
from os_ken.lib import dpid as dpidlib
LOG = logging.getLogger('test_wsgi')
class _TestController(ControllerBase):
def __init__(self, req, link, data, **config):
super(_TestController, self).__init__(req, link, data, **config)
eq_(data['test_param'], 'foo')
@route('test', '/test/{dpid}',
methods=['GET'], requirements={'dpid': dpidlib.DPID_PATTERN})
def test_get_dpid(self, req, dpid, **_kwargs):
return Response(status=200, body=dpid)
@route('test', '/test')
def test_root(self, req, **_kwargs):
return Response(status=200, body='root')
class Test_wsgi(unittest.TestCase):
""" Test case for wsgi
"""
def setUp(self):
controller_data = {
'test_param': 'foo'
}
self.wsgi_app = WSGIApplication()
self.wsgi_app.register(_TestController, controller_data)
def tearDown(self):
pass
def test_wsgi_decorator_ok(self):
r = self.wsgi_app({'REQUEST_METHOD': 'GET',
'PATH_INFO': '/test/0123456789abcdef'},
lambda s, _: eq_(s, '200 OK'))
eq_(r[0], (b'0123456789abcdef'))
def test_wsgi_decorator_ng_path(self):
self.wsgi_app({'REQUEST_METHOD': 'GET',
'PATH_INFO': '/'},
lambda s, _: eq_(s, '404 Not Found'))
def test_wsgi_decorator_ng_method(self):
# XXX: If response code is "405 Method Not Allowed", it is better.
self.wsgi_app({'REQUEST_METHOD': 'PUT',
'PATH_INFO': '/test/0123456789abcdef'},
lambda s, _: eq_(s, '404 Not Found'))
def test_wsgi_decorator_ng_requirements(self):
# XXX: If response code is "400 Bad Request", it is better.
self.wsgi_app({'REQUEST_METHOD': 'GET',
'PATH_INFO': '/test/hogehoge'},
lambda s, _: eq_(s, '404 Not Found'))
def test_wsgi_decorator_ok_any_method(self):
self.wsgi_app({'REQUEST_METHOD': 'GET',
'PATH_INFO': '/test'},
lambda s, _: eq_(s, '200 OK'))
self.wsgi_app({'REQUEST_METHOD': 'POST',
'PATH_INFO': '/test'},
lambda s, _: eq_(s, '200 OK'))
self.wsgi_app({'REQUEST_METHOD': 'PUT',
'PATH_INFO': '/test'},
lambda s, _: eq_(s, '200 OK'))
r = self.wsgi_app({'REQUEST_METHOD': 'DELETE',
'PATH_INFO': '/test'},
lambda s, _: eq_(s, '200 OK'))
eq_(r[0], b'root')
if __name__ == '__main__':
nose.main(argv=['nosetests', '-s', '-v'], defaultTest=__file__)

View File

@ -2,7 +2,6 @@
import unittest
from nose.tools import ok_, eq_
# from os_ken.app.simple_switch import SimpleSwitch
import logging

View File

@ -2,7 +2,6 @@
import unittest
from nose.tools import ok_, eq_
# from os_ken.app.simple_switch import SimpleSwitch
class TestSample2(unittest.TestCase):

View File

@ -10,5 +10,4 @@ oslo.config>=5.1.0
ovs>=2.8.0 # OVSDB
Routes>=2.3.1 # MIT
six>=1.10.0
tinyrpc>=0.6 # RPC library, BGP speaker(net_cntl)
WebOb>=1.8.2 # wsgi

View File

@ -8,5 +8,4 @@ oslo.config>=5.1.0
ovs>=2.8.0 # OVSDB
Routes>=2.3.1 # wsgi
six>=1.10.0
tinyrpc # RPC library, BGP speaker(net_cntl)
WebOb>=1.8.2 # wsgi