Compare commits

..

No commits in common. "master" and "v3.25" have entirely different histories.

1182 changed files with 28883 additions and 127316 deletions

View File

@ -1,25 +0,0 @@
name: Unit tests
on: [push, pull_request]
jobs:
unit-tests:
name: Unit tests
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.5, 3.6, 3.7, 3.8, 3.9]
steps:
- name: Checkout repo
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade -r pip-requirements.txt
pip install tox tox-gh-actions coveralls
bash ryu/tests/integrated/common/install_docker_test_pkg_for_github_actions.sh
- name: Test with tox
run: NOSE_VERBOSE=0 tox

View File

@ -10,6 +10,6 @@
# W0614: Unused import %s from wildcard import
# R0801: Similar lines in %s files
disable=C0111,W0511,W0142,E0602,C0103,E1101,R0903,W0614,R0801
msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
output-format=parseable
reports=yes
files-output=no

View File

@ -1,11 +0,0 @@
version: 2
build:
image: latest
python:
version: 3.6
install:
- method: pip
path: .
sphinx:
configuration: doc/source/conf.py
formats: all

View File

@ -1,15 +0,0 @@
{
"separateMajorMinor": false,
"schedule": [
"after 10pm every weekday",
"before 5am every weekday",
"every weekend"
],
"timezone": "Pacific/Auckland",
"extends": [
"config:base",
":prHourlyLimit1",
":preserveSemverRanges",
"docker:enableMajor"
]
}

View File

@ -1,9 +0,0 @@
---
linters:
flake8:
python: 3
max-line-length: 120
pep8:
python: 3
max-line-length: 120
py3k:

17
.travis.yml Normal file
View File

@ -0,0 +1,17 @@
language: python
python:
- "2.7"
env:
- TOX_ENV=py26
- TOX_ENV=py27
- TOX_ENV=py34
- TOX_ENV=pep8
install:
- "pip install tox"
script:
- tox -e $TOX_ENV
sudo: false

View File

@ -5,59 +5,46 @@ How to Get Your Change Into Ryu
Submitting a change
===================
To send patches to ryu, please make a
`pull request <https://github.com/faucetsdn/ryu>`_ on GitHub.
Send patches to ryu-devel@lists.sourceforge.net. Please don't use 'pull
request' on github. We expect you to send a patch in Linux kernel
development style. If you are not familiar with it, please read the
following document:
Please check your changes with autopep8, pycodestyle(pep8) and running
unit tests to make sure that they don't break the existing features.
The following command does all for you.
https://www.kernel.org/doc/Documentation/SubmittingPatches
.. code-block:: bash
Please check your changes with pep8 and run unittests to make sure
that they don't break the existing features. The following command
does both for you:
# Install dependencies of tests
$ pip install -r tools/test-requires
fujita@rose:~/git/ryu$ ./run_tests.sh
# Execute autopep8
# Also, it is convenient to add settings of your editor or IDE for
# applying autopep8 automatically.
$ autopep8 --recursive --in-place ryu/
# Execute unit tests and pycodestyle(pep8)
$ ./run_tests.sh
Of course, you are encouraged to add unit tests when you add new
Of course, you are encouraged to add unittests when you add new
features (it's not a must though).
Python version and libraries
============================
* Python 3.5, 3.6, 3.7, 3.8, 3.9:
* Python 2.6+
As RHEL 6 adopted python 2.6, features only for 2.7+ should be avoided.
Ryu supports multiple Python versions. CI tests on GitHub Actions is running
on these versions.
* standard library + widely used library:
Basically widely used == OpenStack adopted.
As usual there are exceptions. Or python binding library for other
* standard library + widely used library
Basically widely used == OpenStack adopted
As usual there are exceptions. gevents. Or python binding library for other
component.
Coding style guide
==================
* pep8:
* pep8
As python is used, PEP8 is would be hopefully mandatory for
https://www.python.org/dev/peps/pep-0008/
* pylint:
http://www.python.org/dev/peps/pep-0008/
* pylint
Although pylint is useful for finding bugs, but pylint score not very
important for now because we're still at early development stage.
https://www.pylint.org/
* Google python style guide is very helpful:
http://google.github.io/styleguide/pyguide.html
* Google python style guide is very helpful
http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
* Guidelines derived from Guido's Recommendations:
Guidelines derived from Guido's Recommendations
============================= ================= ========
Type Public Internal
@ -75,11 +62,10 @@ Coding style guide
Local Variables lower_with_under
============================= ================= ========
* OpenStack Nova style guide:
* OpenStack Nova style guide
https://github.com/openstack/nova/blob/master/HACKING.rst
* JSON files:
* JSON files
Ryu source tree has JSON files under ryu/tests/unit/ofproto/json.
They are used by unit tests. To make patches easier to read,
they are normalized using tools/normalize_json.py. Please re-run

View File

@ -1,13 +1,8 @@
**PLEASE READ: RYU NOT CURRENTLY MAINTAINED**
* The Ryu project needs new maintainers - please file an issue if you are able to assist.
* see OpenStack's os-ken (`<https://github.com/openstack/os-ken>`_) for a maintained Ryu alternative.
What's Ryu
==========
Ryu is a component-based software defined networking framework.
Ryu provides software components with well defined API's that make it
Ryu provides software components with well defined API that make it
easy for developers to create new network management and control
applications. Ryu supports various protocols for managing network
devices, such as OpenFlow, Netconf, OF-config, etc. About OpenFlow,
@ -25,11 +20,14 @@ Installing Ryu is quite easy::
If you prefer to install Ryu from the source code::
% git clone https://github.com/faucetsdn/ryu.git
% cd ryu; pip install .
% git clone git://github.com/osrg/ryu.git
% cd ryu; python ./setup.py install
If you want to use Ryu with `OpenStack <http://openstack.org/>`_,
please refer `networking-ofagent project <https://github.com/stackforge/networking-ofagent>`_.
If you want to write your Ryu application, have a look at
`Writing ryu application <http://ryu.readthedocs.io/en/latest/writing_ryu_app.html>`_ document.
`Writing ryu application <http://ryu.readthedocs.org/en/latest/writing_ryu_app.html>`_ document.
After writing your application, just type::
% ryu-manager yourapp.py
@ -38,33 +36,21 @@ After writing your application, just type::
Optional Requirements
=====================
Some functions of ryu require extra packages:
Some functionalities of ryu requires extra packages:
- OF-Config requires lxml and ncclient
- OF-Config requires lxml
- NETCONF requires paramiko
- BGP speaker (SSH console) requires paramiko
- Zebra protocol service (database) requires SQLAlchemy
- BGP speaker (ssh console) requires paramiko
If you want to use these functions, please install the requirements::
If you want to use the functionalities, please install requirements::
% pip install -r tools/optional-requires
Please refer to tools/optional-requires for details.
Prerequisites
=============
If you got some error messages at the installation stage, please confirm
dependencies for building the required Python packages.
On Ubuntu(16.04 LTS or later)::
% apt install gcc python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev zlib1g-dev
% pip install lxml
% pip install paramiko
Support
=======
Ryu Official site is `<https://ryu-sdn.org/>`_.
Ryu Official site is `<http://osrg.github.io/ryu/>`_.
If you have any
questions, suggestions, and patches, the mailing list is available at

10
debian/control vendored
View File

@ -6,7 +6,7 @@ Build-Depends: debhelper (>= 9.0.0), python-all (>= 2.6), python-sphinx
Build-Depends-Indep:
python-eventlet,
python-lxml,
python-msgpack (>= 0.4.0),
python-msgpack (>= 0.3.0),
python-netaddr,
python-oslo.config (>= 1:1.2.0),
python-paramiko,
@ -17,9 +17,9 @@ Build-Depends-Indep:
python-pip,
python-pbr
Standards-Version: 3.9.5
Homepage: https://ryu-sdn.org
Vcs-Git: git://github.com/faucetsdn/ryu.git
Vcs-Browser: https://github.com/faucetsdn/ryu
Homepage: http://osrg.github.io/ryu/
Vcs-Git: git://github.com/osrg/ryu.git
Vcs-Browser: http://github.com/osrg/ryu
XS-Python-Version: >= 2.6
Package: python-ryu
@ -28,7 +28,7 @@ Section: python
Depends:
python-eventlet,
python-lxml,
python-msgpack (>= 0.4.0),
python-msgpack (>= 0.3.0),
python-netaddr,
python-oslo.config (>= 1:1.2.0),
python-paramiko,

2
debian/copyright vendored
View File

@ -1,6 +1,6 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: ryu
Source: http://github.com/faucetsdn/ryu
Source: http://github.com/osrg/ryu
Files: *
Copyright: 2014 Ryu Project Team <ryu-devel@lists.sourceforge.net>

View File

@ -11,5 +11,3 @@ Others provide some functionalities to other Ryu applications.
app/ofctl.rst
app/ofctl_rest.rst
app/rest_vtep.rst
app/bgp_application.rst

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -74,14 +74,6 @@ ryu.ofproto.ofproto_v1_4_parser
-------------------------------
.. automodule:: ryu.ofproto.ofproto_v1_4_parser
ryu.ofproto.ofproto_v1_5
------------------------
.. automodule:: ryu.ofproto.ofproto_v1_5
ryu.ofproto.ofproto_v1_5_parser
-------------------------------
.. automodule:: ryu.ofproto.ofproto_v1_5_parser
Ryu applications
================
@ -94,6 +86,42 @@ ryu.app.simple_switch
---------------------
.. automodule:: ryu.app.simple_switch
ryu.app.simple_isolation
------------------------
.. automodule:: ryu.app.simple_isolation
ryu.app.simple_vlan
-------------------
.. automodule:: ryu.app.simple_vlan
ryu.app.gre_tunnel
------------------
.. automodule:: ryu.app.gre_tunnel
ryu.app.tunnel_port_updater
---------------------------
.. automodule:: ryu.app.tunnel_port_updater
ryu.app.quantum_adapter
-----------------------
.. automodule:: ryu.app.quantum_adapter
ryu.app.rest
------------
.. automodule:: ryu.app.rest
ryu.app.rest_conf_switch
------------------------
.. automodule:: ryu.app.rest_conf_switch
ryu.app.rest_quantum
--------------------
.. automodule:: ryu.app.rest_quantum
ryu.app.rest_tunnel
-------------------
.. automodule:: ryu.app.rest_tunnel
ryu.topology
------------
.. automodule:: ryu.topology

View File

@ -94,7 +94,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'sphinx_rtd_theme'
html_theme = 'haiku'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@ -132,7 +132,6 @@ html_static_path = ['_static']
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# (Deprecated since version 1.6)
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

View File

@ -10,5 +10,4 @@ Writing Your Ryu Application
ryu_app_api.rst
library.rst
ofproto_ref.rst
nicira_ext_ref.rst
api_ref.rst

View File

@ -1,2 +0,0 @@
[parsers]
smart_quotes: false

View File

@ -16,6 +16,7 @@ Contents:
developing.rst
configuration.rst
tests.rst
using_with_openstack.rst
snort_integrate.rst
app.rst

View File

@ -9,10 +9,7 @@ Ryu provides some useful library for your network applications.
library_packet.rst
library_packet_ref.rst
library_pcap.rst
library_of_config.rst
library_bgp_speaker.rst
library_bgp_speaker_ref.rst
library_mrt.rst
library_ovsdb_manager.rst
library_ovsdb.rst

View File

@ -6,8 +6,8 @@ Introduction
============
Ryu BGP speaker library helps you to enable your code to speak BGP
protocol. The library supports IPv4, IPv4 MPLS-labeled VPN, IPv6
MPLS-labeled VPN and L2VPN EVPN address families.
protocol. The library supports ipv4, ipv4 vpn, and ipv6 vpn address
families.
Example
=======

View File

@ -1,28 +0,0 @@
****************
MRT file library
****************
Introduction
============
Ryu MRT file library helps you to read/write MRT
(Multi-Threaded Routing Toolkit) Routing Information Export Format
[`RFC6396`_].
.. _RFC6396: https://tools.ietf.org/html/rfc6396
Reading MRT file
================
For loading the routing information contained in MRT files, you can use
mrtlib.Reader.
.. autoclass:: ryu.lib.mrtlib.Reader
Writing MRT file
================
For dumping the routing information which your RyuApp generated, you can use
mrtlib.Writer.
.. autoclass:: ryu.lib.mrtlib.Writer

View File

@ -1,76 +0,0 @@
*************
OVSDB library
*************
Path: ``ryu.lib.ovs``
Similar to the :doc:`library_ovsdb_manager`, this library enables your
application to speak the OVSDB protocol (RFC7047_), but differ from the
:doc:`library_ovsdb_manager`, this library will initiate connections from
controller side as ovs-vsctl_ command does.
Please make sure that your devices are listening on either the Unix domain
socket or TCP/SSL port before calling the APIs of this library.
.. code-block:: bash
# Show current configuration
$ ovs-vsctl get-manager
# Set TCP listen address
$ ovs-vsctl set-manager "ptcp:6640"
See manpage of ovs-vsctl_ command for more details.
.. _RFC7047: https://tools.ietf.org/html/rfc7047
.. _ovs-vsctl: http://openvswitch.org/support/dist-docs/ovs-vsctl.8.txt
Basic Usage
===========
1. Instantiate :py:mod:`ryu.lib.ovs.vsctl.VSCtl`.
2. Construct commands with :py:mod:`ryu.lib.ovs.vsctl.VSCtlCommand`.
The syntax is almost the same as ovs-vsctl_ command.
3. Execute commands via :py:mod:`ryu.lib.ovs.vsctl.VSCtl.run_command`.
Example
-------
.. code-block:: python
from ryu.lib.ovs import vsctl
OVSDB_ADDR = 'tcp:127.0.0.1:6640'
ovs_vsctl = vsctl.VSCtl(OVSDB_ADDR)
# Equivalent to
# $ ovs-vsctl show
command = vsctl.VSCtlCommand('show')
ovs_vsctl.run_command([command])
print(command)
# >>> VSCtlCommand(args=[],command='show',options=[],result='830d781f-c3c8-4b4f-837e-106e1b33d058\n ovs_version: "2.8.90"\n')
# Equivalent to
# $ ovs-vsctl list Port s1-eth1
command = vsctl.VSCtlCommand('list', ('Port', 's1-eth1'))
ovs_vsctl.run_command([command])
print(command)
# >>> VSCtlCommand(args=('Port', 's1-eth1'),command='list',options=[],result=[<ovs.db.idl.Row object at 0x7f525fb682e8>])
print(command.result[0].name)
# >>> s1-eth1
API Reference
=============
ryu.lib.ovs.vsctl
-----------------
.. automodule:: ryu.lib.ovs.vsctl
:members:
ryu.lib.ovs.bridge
------------------
.. automodule:: ryu.lib.ovs.bridge
:members:

View File

@ -2,8 +2,6 @@
OVSDB Manager library
*********************
Path: ``ryu.services.protocols.ovsdb``
Introduction
============
@ -11,47 +9,17 @@ Ryu OVSDB Manager library allows your code to interact with devices
speaking the OVSDB protocol. This enables your code to perform remote
management of the devices and react to topology changes on them.
Please note this library will spawn a server listening on the port 6640 (the
IANA registered for OVSDB protocol), but does not initiate connections from
controller side.
Then, to make your devices connect to Ryu, you need to tell the controller IP
address and port to your devices.
.. code-block:: bash
# Show current configuration
$ ovs-vsctl get-manager
# Set manager (controller) address
$ ovs-vsctl set-manager "tcp:127.0.0.1:6640"
# If you want to specify IPv6 address, wrap ip with brackets
$ ovs-vsctl set-manager "tcp:[::1]:6640"
Also this library identifies the devices by "system-id" which should be unique,
persistent identifier among all devices connecting to a single controller.
Please make sure "system-id" is configured before connecting.
.. code-block:: bash
# Show current configuration
$ ovs-vsctl get Open_vSwitch . external_ids:system-id
# Set system-id manually
$ ovs-vsctl set Open_vSwitch . external_ids:system-id=<SYSTEM-ID>
Example
=======
The following logs all new OVSDB connections in "handle_new_ovsdb_connection"
and also provides the API "create_port" for creating a port on a bridge.
The following logs all new OVSDB connections and allows creating a port
on a bridge.
.. code-block:: python
import uuid
from ryu.base import app_manager
from ryu.controller.handler import set_ev_cls
from ryu.services.protocols.ovsdb import api as ovsdb
from ryu.services.protocols.ovsdb import event as ovsdb_event
@ -60,22 +28,16 @@ and also provides the API "create_port" for creating a port on a bridge.
@set_ev_cls(ovsdb_event.EventNewOVSDBConnection)
def handle_new_ovsdb_connection(self, ev):
system_id = ev.system_id
address = ev.client.address
self.logger.info(
'New OVSDB connection from system-id=%s, address=%s',
system_id, address)
self.logger.info('New OVSDB connection from system id %s',
systemd_id)
# Example: If device has bridge "s1", add port "s1-eth99"
if ovsdb.bridge_exists(self, system_id, "s1"):
self.create_port(system_id, "s1", "s1-eth99")
def create_port(self, system_id, bridge_name, name):
def create_port(self, systemd_id, bridge_name, name):
new_iface_uuid = uuid.uuid4()
new_port_uuid = uuid.uuid4()
bridge = ovsdb.row_by_name(self, system_id, bridge_name)
def _create_port(tables, insert):
bridge = ovsdb.row_by_name(self, system_id, bridge_name)
iface = insert(tables['Interface'], new_iface_uuid)
iface.name = name
iface.type = 'internal'
@ -84,9 +46,9 @@ and also provides the API "create_port" for creating a port on a bridge.
port.name = name
port.interfaces = [iface]
bridge.ports = bridge.ports + [port]
brdige.ports = bridfe.ports + [port]
return new_port_uuid, new_iface_uuid
return (new_port_uuid, new_iface_uuid)
req = ovsdb_event.EventModifyRequest(system_id, _create_port)
rep = self.send_request(req)
@ -96,4 +58,4 @@ and also provides the API "create_port" for creating a port on a bridge.
name, bridge, rep.status)
return None
return rep.insert_uuids[new_port_uuid]
return reply.insert_uuid[new_port_uuid]

View File

@ -14,15 +14,119 @@ Stream Parser class
.. automodule:: ryu.lib.packet.stream_parser
:members:
List of the sub-classes:
- :py:mod:`ryu.lib.packet.bgp.StreamParser`
.. autoclass:: ryu.lib.packet.bgp.StreamParser
:members:
Protocol Header classes
=======================
.. toctree::
:glob:
.. automodule:: ryu.lib.packet.packet_base
:members:
library_packet_ref/packet_base
library_packet_ref/*
.. automodule:: ryu.lib.packet.ethernet
:members:
.. automodule:: ryu.lib.packet.vlan
:members:
.. automodule:: ryu.lib.packet.pbb
:members:
.. automodule:: ryu.lib.packet.mpls
:members:
.. automodule:: ryu.lib.packet.arp
:members:
.. automodule:: ryu.lib.packet.ipv4
:members:
.. automodule:: ryu.lib.packet.icmp
:members:
.. automodule:: ryu.lib.packet.ipv6
:members:
.. automodule:: ryu.lib.packet.icmpv6
:members:
.. automodule:: ryu.lib.packet.cfm
:members:
.. automodule:: ryu.lib.packet.tcp
:members:
.. automodule:: ryu.lib.packet.udp
:members:
.. autoclass:: ryu.lib.packet.dhcp.dhcp
:members:
.. autoclass:: ryu.lib.packet.dhcp.options
:members:
.. autoclass:: ryu.lib.packet.dhcp.option
:members:
.. autoclass:: ryu.lib.packet.vrrp.vrrp
:members:
.. autoclass:: ryu.lib.packet.vrrp.vrrpv2
:members:
.. autoclass:: ryu.lib.packet.vrrp.vrrpv3
:members:
.. autoclass:: ryu.lib.packet.slow.slow
:members:
.. autoclass:: ryu.lib.packet.slow.lacp
:members:
.. autoclass:: ryu.lib.packet.llc.llc
:members:
.. autoclass:: ryu.lib.packet.llc.ControlFormatI
:members:
.. autoclass:: ryu.lib.packet.llc.ControlFormatS
:members:
.. autoclass:: ryu.lib.packet.llc.ControlFormatU
:members:
.. autoclass:: ryu.lib.packet.bpdu.bpdu
:members:
.. autoclass:: ryu.lib.packet.bpdu.ConfigurationBPDUs
:members:
.. autoclass:: ryu.lib.packet.bpdu.TopologyChangeNotificationBPDUs
:members:
.. autoclass:: ryu.lib.packet.bpdu.RstBPDUs
:members:
.. autoclass:: ryu.lib.packet.igmp.igmp
:members:
.. autoclass:: ryu.lib.packet.igmp.igmpv3_query
:members:
.. autoclass:: ryu.lib.packet.igmp.igmpv3_report
:members:
.. autoclass:: ryu.lib.packet.igmp.igmpv3_report_group
:members:
.. autoclass:: ryu.lib.packet.bgp.BGPMessage
:members:
.. autoclass:: ryu.lib.packet.bgp.BGPOpen
:members:
.. autoclass:: ryu.lib.packet.bgp.BGPUpdate
:members:
.. autoclass:: ryu.lib.packet.bgp.BGPKeepAlive
:members:
.. autoclass:: ryu.lib.packet.bgp.BGPNotification
:members:
.. automodule:: ryu.lib.packet.sctp
:members:
.. autoclass:: ryu.lib.packet.bfd.bfd
:members:
.. autoclass:: ryu.lib.packet.bfd.SimplePassword
:members:
.. autoclass:: ryu.lib.packet.bfd.KeyedMD5
:members:
.. autoclass:: ryu.lib.packet.bfd.MeticulousKeyedMD5
:members:
.. autoclass:: ryu.lib.packet.bfd.KeyedSHA1
:members:
.. autoclass:: ryu.lib.packet.bfd.MeticulousKeyedSHA1
:members:

View File

@ -1,6 +0,0 @@
***
ARP
***
.. automodule:: ryu.lib.packet.arp
:members:

View File

@ -1,6 +0,0 @@
*****************
Packet Base Class
*****************
.. automodule:: ryu.lib.packet.packet_base
:members:

View File

@ -1,6 +0,0 @@
***
BFD
***
.. automodule:: ryu.lib.packet.bfd
:members:

View File

@ -1,6 +0,0 @@
***
BGP
***
.. automodule:: ryu.lib.packet.bgp
:members:

View File

@ -1,6 +0,0 @@
***
BMP
***
.. automodule:: ryu.lib.packet.bmp
:members:

View File

@ -1,6 +0,0 @@
****
BPDU
****
.. automodule:: ryu.lib.packet.bpdu
:members:

View File

@ -1,6 +0,0 @@
***
CFM
***
.. automodule:: ryu.lib.packet.cfm
:members:

View File

@ -1,6 +0,0 @@
****
DHCP
****
.. automodule:: ryu.lib.packet.dhcp
:members:

View File

@ -1,6 +0,0 @@
*****
DHCP6
*****
.. automodule:: ryu.lib.packet.dhcp6
:members:

View File

@ -1,6 +0,0 @@
********
Ethernet
********
.. automodule:: ryu.lib.packet.ethernet
:members:

View File

@ -1,6 +0,0 @@
******
Geneve
******
.. automodule:: ryu.lib.packet.geneve
:members:

View File

@ -1,6 +0,0 @@
***
GRE
***
.. automodule:: ryu.lib.packet.gre
:members:

View File

@ -1,6 +0,0 @@
****
ICMP
****
.. automodule:: ryu.lib.packet.icmp
:members:

View File

@ -1,6 +0,0 @@
******
ICMPv6
******
.. automodule:: ryu.lib.packet.icmpv6
:members:

View File

@ -1,6 +0,0 @@
****
IGMP
****
.. automodule:: ryu.lib.packet.igmp
:members:

View File

@ -1,6 +0,0 @@
****
IPv4
****
.. automodule:: ryu.lib.packet.ipv4
:members:

View File

@ -1,6 +0,0 @@
****
IPv6
****
.. automodule:: ryu.lib.packet.ipv6
:members:

View File

@ -1,6 +0,0 @@
***
LLC
***
.. automodule:: ryu.lib.packet.llc
:members:

View File

@ -1,6 +0,0 @@
****
LLDP
****
.. automodule:: ryu.lib.packet.lldp
:members:

View File

@ -1,6 +0,0 @@
****
MPLS
****
.. automodule:: ryu.lib.packet.mpls
:members:

View File

@ -1,6 +0,0 @@
********
OpenFlow
********
.. automodule:: ryu.lib.packet.openflow
:members:

View File

@ -1,6 +0,0 @@
****
OSPF
****
.. automodule:: ryu.lib.packet.ospf
:members:

View File

@ -1,6 +0,0 @@
***
PBB
***
.. automodule:: ryu.lib.packet.pbb
:members:

View File

@ -1,6 +0,0 @@
****
SCTP
****
.. automodule:: ryu.lib.packet.sctp
:members:

View File

@ -1,6 +0,0 @@
****
Slow
****
.. automodule:: ryu.lib.packet.slow
:members:

View File

@ -1,6 +0,0 @@
***
TCP
***
.. automodule:: ryu.lib.packet.tcp
:members:

View File

@ -1,6 +0,0 @@
***
UDP
***
.. automodule:: ryu.lib.packet.udp
:members:

View File

@ -1,6 +0,0 @@
****
VLAN
****
.. automodule:: ryu.lib.packet.vlan
:members:

View File

@ -1,6 +0,0 @@
****
VRRP
****
.. automodule:: ryu.lib.packet.vrrp
:members:

View File

@ -1,6 +0,0 @@
*****
VXLAN
*****
.. automodule:: ryu.lib.packet.vxlan
:members:

View File

@ -1,6 +0,0 @@
*****
Zebra
*****
.. automodule:: ryu.lib.packet.zebra
:members:

View File

@ -1,27 +0,0 @@
*****************
PCAP file library
*****************
Introduction
============
Ryu PCAP file library helps you to read/write PCAP file which file
format are described in `The Wireshark Wiki`_.
.. _The Wireshark Wiki: https://wiki.wireshark.org/Development/LibpcapFileFormat
Reading PCAP file
=================
For loading the packet data containing in PCAP files, you can use
pcaplib.Reader.
.. autoclass:: ryu.lib.pcaplib.Reader
Writing PCAP file
=================
For dumping the packet data which your RyuApp received, you can use
pcaplib.Writer.
.. autoclass:: ryu.lib.pcaplib.Writer

View File

@ -1,69 +0,0 @@
***************************
Nicira Extension Structures
***************************
.. _nx_actions_structures:
Nicira Extension Actions Structures
===================================
The followings shows the supported NXAction classes only in OpenFlow1.0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. py:currentmodule:: ryu.ofproto.ofproto_v1_0_parser
.. autoclass:: NXActionSetQueue
.. autoclass:: NXActionDecTtl
.. autoclass:: NXActionPushMpls
.. autoclass:: NXActionPopMpls
.. autoclass:: NXActionSetMplsTtl
.. autoclass:: NXActionDecMplsTtl
.. autoclass:: NXActionSetMplsLabel
.. autoclass:: NXActionSetMplsTc
The followings shows the supported NXAction classes in OpenFlow1.0 or later
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. py:currentmodule:: ryu.ofproto.ofproto_v1_3_parser
.. autoclass:: NXActionPopQueue
.. autoclass:: NXActionRegLoad
.. autoclass:: NXActionRegLoad2
.. autoclass:: NXActionNote
.. autoclass:: NXActionSetTunnel
.. autoclass:: NXActionSetTunnel64
.. autoclass:: NXActionRegMove
.. autoclass:: NXActionResubmit
.. autoclass:: NXActionResubmitTable
.. autoclass:: NXActionOutputReg
.. autoclass:: NXActionOutputReg2
.. autoclass:: NXActionLearn
.. autoclass:: NXActionExit
.. autoclass:: NXActionController
.. autoclass:: NXActionController2
.. autoclass:: NXActionDecTtlCntIds
.. autoclass:: NXActionStackPush
.. autoclass:: NXActionStackPop
.. autoclass:: NXActionSample
.. autoclass:: NXActionSample2
.. autoclass:: NXActionFinTimeout
.. autoclass:: NXActionConjunction
.. autoclass:: NXActionMultipath
.. autoclass:: NXActionBundle
.. autoclass:: NXActionBundleLoad
.. autoclass:: NXActionCT
.. autoclass:: NXActionNAT
.. autoclass:: NXActionOutputTrunc
.. autoclass:: NXActionDecNshTtl
.. autoclass:: NXFlowSpecMatch
.. autoclass:: NXFlowSpecLoad
.. autoclass:: NXFlowSpecOutput
.. autofunction:: ryu.ofproto.nicira_ext.ofs_nbits
.. _nx_match_structures:
Nicira Extended Match Structures
================================
.. automodule:: ryu.ofproto.nicira_ext

View File

@ -8,8 +8,6 @@ OpenFlow protocol API Reference
:maxdepth: 3
ofproto_base.rst
ofproto_v1_0_ref.rst
ofproto_v1_2_ref.rst
ofproto_v1_3_ref.rst
ofproto_v1_4_ref.rst
ofproto_v1_5_ref.rst

View File

@ -1,294 +0,0 @@
*************************************
OpenFlow v1.0 Messages and Structures
*************************************
.. py:currentmodule:: ryu.ofproto.ofproto_v1_0_parser
Controller-to-Switch Messages
=============================
Handshake
---------
.. autoclass:: OFPFeaturesRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-5-features_request.packet.json
.. autoclass:: OFPSwitchFeatures
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-6-ofp_switch_features.packet.json
Switch Configuration
--------------------
.. autoclass:: OFPSetConfig
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-7-ofp_set_config.packet.json
.. autoclass:: OFPGetConfigRequest
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-8-ofp_get_config_request.packet.json
.. autoclass:: OFPGetConfigReply
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-9-ofp_get_config_reply.packet.json
Modify State Messages
---------------------
.. autoclass:: OFPFlowMod
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-2-ofp_flow_mod.packet.json
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-3-ofp_flow_mod.packet.json
.. autoclass:: OFPPortMod
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-22-ofp_port_mod.packet.json
Queue Configuration Messages
----------------------------
.. autoclass:: OFPQueueGetConfigRequest
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-35-ofp_queue_get_config_request.packet.json
.. autoclass:: OFPQueueGetConfigReply
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-36-ofp_queue_get_config_reply.packet.json
Read State Messages
-------------------
.. autoclass:: OFPDescStatsRequest
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-24-ofp_desc_stats_request.packet.json
.. autoclass:: OFPDescStatsReply
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-0-ofp_desc_stats_reply.packet.json
.. autoclass:: OFPFlowStatsRequest
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-11-ofp_flow_stats_request.packet.json
.. autoclass:: OFPFlowStatsReply
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-12-ofp_flow_stats_reply.packet.json
.. autoclass:: OFPAggregateStatsRequest
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-25-ofp_aggregate_stats_request.packet.json
.. autoclass:: OFPAggregateStatsReply
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-26-ofp_aggregate_stats_reply.packet.json
.. autoclass:: OFPTableStatsRequest
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-27-ofp_table_stats_request.packet.json
.. autoclass:: OFPTableStatsReply
.. XXX commented out because it's too long
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-28-ofp_table_stats_reply.packet.json
.. autoclass:: OFPPortStatsRequest
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-29-ofp_port_stats_request.packet.json
.. autoclass:: OFPPortStatsReply
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-30-ofp_port_stats_reply.packet.json
.. autoclass:: OFPQueueStatsRequest
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-37-ofp_queue_stats_request.packet.json
.. autoclass:: OFPQueueStatsReply
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-38-ofp_queue_stats_reply.packet.json
.. autoclass:: OFPVendorStatsRequest
.. autoclass:: OFPVendorStatsReply
Send Packet Message
-------------------
.. autoclass:: OFPPacketOut
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-1-ofp_packet_out.packet.json
Barrier Message
---------------
.. autoclass:: OFPBarrierRequest
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-17-ofp_barrier_request.packet.json
.. autoclass:: OFPBarrierReply
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-18-ofp_barrier_reply.packet.json
Asynchronous Messages
=====================
Packet-In Message
-----------------
.. autoclass:: OFPPacketIn
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-4-ofp_packet_in.packet.json
Flow Removed Message
--------------------
.. autoclass:: OFPFlowRemoved
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-40-ofp_flow_removed.packet.json
Port Status Message
-------------------
.. autoclass:: OFPPortStatus
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-39-ofp_port_status.packet.json
Error Message
-------------
.. autoclass:: OFPErrorMsg
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-15-ofp_error_msg.packet.json
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-41-ofp_error_msg_vendor.packet.json
Symmetric Messages
==================
Hello
-----
.. autoclass:: OFPHello
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-10-ofp_hello.packet.json
Echo Request
------------
.. autoclass:: OFPEchoRequest
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-13-ofp_echo_request.packet.json
Echo Reply
----------
.. autoclass:: OFPEchoReply
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-14-ofp_echo_reply.packet.json
Vendor
------------
.. autoclass:: OFPVendor
.. JSON Example:
..
.. .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of10/1-16-ofp_vendor.packet.json
Port Structures
===============
.. autoclass:: OFPPhyPort
Flow Match Structure
====================
.. autoclass:: OFPMatch
Action Structures
=================
.. autoclass:: OFPActionHeader
.. autoclass:: OFPAction
.. autoclass:: OFPActionOutput
.. autoclass:: OFPActionVlanVid
.. autoclass:: OFPActionVlanPcp
.. autoclass:: OFPActionStripVlan
.. autoclass:: OFPActionDlAddr
.. autoclass:: OFPActionSetDlSrc
.. autoclass:: OFPActionSetDlDst
.. autoclass:: OFPActionNwAddr
.. autoclass:: OFPActionSetNwSrc
.. autoclass:: OFPActionSetNwDst
.. autoclass:: OFPActionSetNwTos
.. autoclass:: OFPActionTpPort
.. autoclass:: OFPActionSetTpSrc
.. autoclass:: OFPActionSetTpDst
.. autoclass:: OFPActionEnqueue
.. autoclass:: OFPActionVendor

View File

@ -312,11 +312,6 @@ Experimenter
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of12/3-16-ofp_experimenter.packet.json
Port Structures
===============
.. autoclass:: OFPPort
Flow Match Structure
====================

View File

@ -396,11 +396,6 @@ Experimenter
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of13/4-16-ofp_experimenter.packet.json
Port Structures
===============
.. autoclass:: OFPPort
Flow Match Structure
====================

View File

@ -355,7 +355,7 @@ Bundle Messages
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of14/5-70-ofp_bundle_add_msg.packet.json
Set Asynchronous Configuration Message
--------------------------------------
@ -435,8 +435,8 @@ Request Forward Message
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of14/5-71-ofp_requestforward.packet.json
Symmetric Messages
==================
@ -487,11 +487,6 @@ Experimenter
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of14/5-16-ofp_experimenter.packet.json
Port Structures
===============
.. autoclass:: OFPPort
Flow Match Structure
====================

View File

@ -1,577 +0,0 @@
*************************************
OpenFlow v1.5 Messages and Structures
*************************************
.. py:currentmodule:: ryu.ofproto.ofproto_v1_5_parser
Controller-to-Switch Messages
=============================
Handshake
---------
.. autoclass:: OFPFeaturesRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-features_request.packet.json
.. autoclass:: OFPSwitchFeatures
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-features_reply.packet.json
Switch Configuration
--------------------
.. autoclass:: OFPSetConfig
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-set_config.packet.json
.. autoclass:: OFPGetConfigRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-get_config_request.packet.json
.. autoclass:: OFPGetConfigReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-get_config_reply.packet.json
Modify State Messages
---------------------
.. autoclass:: OFPTableMod
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-table_mod.packet.json
.. autoclass:: OFPFlowMod
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-flow_mod.packet.json
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-flow_mod_conjunction.packet.json
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-flow_mod_match_conj.packet.json
.. autoclass:: OFPGroupMod
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-group_mod.packet.json
.. autoclass:: OFPPortMod
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-port_mod.packet.json
.. autoclass:: OFPMeterMod
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-meter_mod.packet.json
Multipart Messages
------------------
.. autoclass:: OFPDescStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-desc_request.packet.json
.. autoclass:: OFPDescStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-desc_reply.packet.json
.. autoclass:: OFPFlowDescStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-flow_desc_request.packet.json
.. autoclass:: OFPFlowDescStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-flow_desc_reply.packet.json
.. autoclass:: OFPFlowStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-flow_stats_request.packet.json
.. autoclass:: OFPFlowStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-flow_stats_reply.packet.json
.. autoclass:: OFPAggregateStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-aggregate_stats_request.packet.json
.. autoclass:: OFPAggregateStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-aggregate_stats_reply.packet.json
.. autoclass:: OFPPortStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-port_stats_request.packet.json
.. autoclass:: OFPPortStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-port_stats_reply.packet.json
.. autoclass:: OFPPortDescStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-port_desc_request.packet.json
.. autoclass:: OFPPortDescStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-port_desc_reply.packet.json
.. autoclass:: OFPQueueStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-queue_stats_request.packet.json
.. autoclass:: OFPQueueStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-queue_stats_reply.packet.json
.. autoclass:: OFPQueueDescStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-queue_desc_request.packet.json
.. autoclass:: OFPQueueDescStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-queue_desc_reply.packet.json
.. autoclass:: OFPGroupStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-group_stats_request.packet.json
.. autoclass:: OFPGroupStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-group_stats_reply.packet.json
.. autoclass:: OFPGroupDescStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-group_desc_request.packet.json
.. autoclass:: OFPGroupDescStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-group_desc_reply.packet.json
.. autoclass:: OFPGroupFeaturesStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-group_features_request.packet.json
.. autoclass:: OFPGroupFeaturesStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-group_features_reply.packet.json
.. autoclass:: OFPMeterStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-meter_stats_request.packet.json
.. autoclass:: OFPMeterStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-meter_stats_reply.packet.json
.. autoclass:: OFPMeterDescStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-meter_desc_request.packet.json
.. autoclass:: OFPMeterDescStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-meter_desc_reply.packet.json
.. autoclass:: OFPMeterFeaturesStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-meter_features_request.packet.json
.. autoclass:: OFPMeterFeaturesStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-meter_features_reply.packet.json
.. autoclass:: OFPControllerStatusStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-controller_status_request.packet.json
.. autoclass:: OFPControllerStatusStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-controller_status_reply.packet.json
.. autoclass:: OFPTableStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-table_stats_request.packet.json
.. autoclass:: OFPTableStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-table_stats_reply.packet.json
.. autoclass:: OFPTableDescStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-table_desc_request.packet.json
.. autoclass:: OFPTableDescStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-table_desc_reply.packet.json
.. autoclass:: OFPTableFeaturesStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-table_features_request.packet.json
.. autoclass:: OFPTableFeaturesStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-table_features_reply.packet.json
.. autoclass:: OFPFlowMonitorRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-flow_monitor_request.packet.json
.. autoclass:: OFPFlowMonitorReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-flow_monitor_reply.packet.json
.. autoclass:: OFPBundleFeaturesStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-bundle_features_request.packet.json
.. autoclass:: OFPBundleFeaturesStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-bundle_features_reply.packet.json
.. autoclass:: OFPExperimenterStatsRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-experimenter_request.packet.json
.. autoclass:: OFPExperimenterStatsReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-experimenter_reply.packet.json
Packet-Out Message
------------------
.. autoclass:: OFPPacketOut
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-packet_out.packet.json
Barrier Message
---------------
.. autoclass:: OFPBarrierRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-barrier_request.packet.json
.. autoclass:: OFPBarrierReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-barrier_reply.packet.json
Role Request Message
--------------------
.. autoclass:: OFPRoleRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-role_request.packet.json
.. autoclass:: OFPRoleReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-role_reply.packet.json
Bundle Messages
---------------
.. autoclass:: OFPBundleCtrlMsg
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-bundle_ctrl.packet.json
.. autoclass:: OFPBundleAddMsg
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-bundle_add.packet.json
Set Asynchronous Configuration Message
--------------------------------------
.. autoclass:: OFPSetAsync
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-set_async.packet.json
.. autoclass:: OFPGetAsyncRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-get_async_request.packet.json
.. autoclass:: OFPGetAsyncReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-get_async_reply.packet.json
Asynchronous Messages
=====================
Packet-In Message
-----------------
.. autoclass:: OFPPacketIn
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-packet_in.packet.json
Flow Removed Message
--------------------
.. autoclass:: OFPFlowRemoved
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-flow_removed.packet.json
Port Status Message
-------------------
.. autoclass:: OFPPortStatus
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-port_status.packet.json
Controller Role Status Message
------------------------------
.. autoclass:: OFPRoleStatus
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-role_status.packet.json
Table Status Message
--------------------
.. autoclass:: OFPTableStatus
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-table_status.packet.json
Request Forward Message
-----------------------
.. autoclass:: OFPRequestForward
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-requestforward.packet.json
Controller Status Message
-------------------------
.. autoclass:: OFPControllerStatus
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-controller_status.packet.json
Symmetric Messages
==================
Hello
-----
.. autoclass:: OFPHello
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-hello.packet.json
.. autoclass:: OFPHelloElemVersionBitmap
Echo Request
------------
.. autoclass:: OFPEchoRequest
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-echo_request.packet.json
Echo Reply
----------
.. autoclass:: OFPEchoReply
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-echo_reply.packet.json
Error Message
-------------
.. autoclass:: OFPErrorMsg
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-error_msg.packet.json
Experimenter
------------
.. autoclass:: OFPExperimenter
JSON Example:
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of15/libofproto-OFP15-error_msg_experimenter.packet.json
Port Structures
===============
.. autoclass:: OFPPort
Flow Match Structure
====================
.. autoclass:: OFPMatch
Flow Stats Structures
=====================
.. autoclass:: OFPStats
Flow Instruction Structures
===========================
.. autoclass:: OFPInstructionGotoTable
.. autoclass:: OFPInstructionWriteMetadata
.. autoclass:: OFPInstructionActions
.. autoclass:: OFPInstructionStatTrigger
Action Structures
=================
.. autoclass:: OFPActionOutput
.. autoclass:: OFPActionCopyTtlOut
.. autoclass:: OFPActionCopyTtlIn
.. autoclass:: OFPActionSetMplsTtl
.. autoclass:: OFPActionDecMplsTtl
.. autoclass:: OFPActionPushVlan
.. autoclass:: OFPActionPopVlan
.. autoclass:: OFPActionPushMpls
.. autoclass:: OFPActionPopMpls
.. autoclass:: OFPActionSetQueue
.. autoclass:: OFPActionGroup
.. autoclass:: OFPActionSetNwTtl
.. autoclass:: OFPActionDecNwTtl
.. autoclass:: OFPActionSetField
.. autoclass:: OFPActionPushPbb
.. autoclass:: OFPActionPopPbb
.. autoclass:: OFPActionCopyField
.. autoclass:: OFPActionMeter
.. autoclass:: OFPActionExperimenter
Controller Status Structure
===========================
.. autoclass:: OFPControllerStatusStats

View File

@ -11,7 +11,7 @@ Threads, events, and event queues
Ryu applications are single-threaded entities which implement
various functionalities in Ryu. Events are messages between them.
Ryu applications send asynchronous events to each other.
Ryu applications send asynchronous events each other.
Besides that, there are some Ryu-internal event sources which
are not Ryu applications. One of examples of such event sources
is OpenFlow controller.
@ -22,11 +22,11 @@ between Ryu applications.
Each Ryu application has a receive queue for events.
The queue is FIFO and preserves the order of events.
Each Ryu application has a thread for event processing.
The thread keeps draining the receive queue by dequeueing an event
The thread keep draining the receive queue by dequeueing an event
and calling the appropritate event handler for the event type.
Because the event handler is called in the context of
the event processing thread, it should be careful when blocking.
While an event handler is blocked, no further events for
the event processing thread, it should be careful for blocking.
I.e. while an event handler is blocked, no further events for
the Ryu application will be processed.
There are kinds of events which are used to implement synchronous
@ -82,10 +82,20 @@ For example, EventOFPPacketIn for packet-in message.
The OpenFlow controller part of Ryu automatically decodes OpenFlow messages
received from switches and send these events to Ryu applications which
expressed an interest using ryu.controller.handler.set_ev_cls.
OpenFlow event classes are subclasses of the following class.
OpenFlow event classes have at least the following attributes.
.. autoclass:: ryu.controller.ofp_event.EventOFPMsgBase
.. tabularcolumns:: |l|L|
============ =============================================================
Attribute Description
============ =============================================================
msg An object which describes the corresponding OpenFlow message.
msg.datapath A ryu.controller.controller.Datapath instance which describes
an OpenFlow switch from which we received this OpenFlow message.
============ =============================================================
The msg object has some more additional members whose values are extracted
from the original OpenFlow message.
See :ref:`ofproto_ref` for more info about OpenFlow messages.
ryu.base.app_manager.RyuApp
@ -93,87 +103,267 @@ ryu.base.app_manager.RyuApp
See :ref:`api_ref`.
ryu.controller.handler.set_ev_cls
=================================
ryu.controller.handler.set_ev_cls(ev_cls, dispatchers=None)
===========================================================
.. autofunction:: ryu.controller.handler.set_ev_cls
A decorator for Ryu application to declare an event handler.
Decorated method will become an event handler.
ev_cls is an event class whose instances this RyuApp wants to receive.
dispatchers argument specifies one of the following negotiation phases
(or a list of them) for which events should be generated for this handler.
Note that, in case an event changes the phase, the phase before the change
is used to check the interest.
.. tabularcolumns:: |l|L|
=========================================== ==================================
Negotiation phase Description
=========================================== ==================================
ryu.controller.handler.HANDSHAKE_DISPATCHER Sending and waiting for hello
message
ryu.controller.handler.CONFIG_DISPATCHER Version negotiated and sent
features-request message
ryu.controller.handler.MAIN_DISPATCHER Switch-features message received
and sent set-config message
ryu.controller.handler.DEAD_DISPATCHER Disconnect from the peer. Or
disconnecting due to some
unrecoverable errors.
=========================================== ==================================
ryu.controller.controller.Datapath
==================================
.. autoclass:: ryu.controller.controller.Datapath
A class to describe an OpenFlow switch connected to this controller.
An instance has the following attributes.
.. tabularcolumns:: |l|L|
====================================== =======================================
Attribute Description
====================================== =======================================
id 64-bit OpenFlow Datapath ID.
Only available for
ryu.controller.handler.MAIN_DISPATCHER
phase.
ofproto A module which exports OpenFlow
definitions, mainly constants appeared
in the specification, for the
negotiated OpenFlow version. For
example, ryu.ofproto.ofproto_v1_0 for
OpenFlow 1.0.
ofproto_parser A module which exports OpenFlow wire
message encoder and decoder for the
negotiated OpenFlow version. For
example, ryu.ofproto.ofproto_v1_0_parser
for OpenFlow 1.0.
ofproto_parser.OFPxxxx(datapath, ....) A callable to prepare an OpenFlow
message for the given switch. It can
be sent with Datapath.send_msg later.
xxxx is a name of the message. For
example OFPFlowMod for flow-mod
message. Arguemnts depend on the
message.
set_xid(self, msg) Generate an OpenFlow XID and put it
in msg.xid.
send_msg(self, msg) Queue an OpenFlow message to send to
the corresponding switch. If msg.xid
is None, set_xid is automatically
called on the message before queueing.
send_packet_out deprecated
send_flow_mod deprecated
send_flow_del deprecated
send_delete_all_flows deprecated
send_barrier Queue an OpenFlow barrier message to
send to the switch.
send_nxt_set_flow_format deprecated
is_reserved_port deprecated
====================================== =======================================
ryu.controller.event.EventBase
==============================
.. autoclass:: ryu.controller.event.EventBase
The base of all event classes.
A Ryu application can define its own event type by creating a subclass.
ryu.controller.event.EventRequestBase
=====================================
.. autoclass:: ryu.controller.event.EventRequestBase
The base class for synchronous request for RyuApp.send_request.
ryu.controller.event.EventReplyBase
===================================
.. autoclass:: ryu.controller.event.EventReplyBase
The base class for synchronous request reply for RyuApp.send_reply.
ryu.controller.ofp_event.EventOFPStateChange
============================================
.. autoclass:: ryu.controller.ofp_event.EventOFPStateChange
An event class for negotiation phase change notification.
An instance of this class is sent to observer after changing
the negotiation phase.
An instance has at least the following attributes.
ryu.controller.ofp_event.EventOFPPortStateChange
================================================
.. autoclass:: ryu.controller.ofp_event.EventOFPPortStateChange
========= ====================================================================
Attribute Description
========= ====================================================================
datapath ryu.controller.controller.Datapath instance of the switch
========= ====================================================================
ryu.controller.dpset.EventDP
============================
.. autoclass:: ryu.controller.dpset.EventDP
An event class to notify connect/disconnect of a switch.
For OpenFlow switches, one can get the same notification by observing
ryu.controller.ofp_event.EventOFPStateChange.
An instance has at least the following attributes.
========= ====================================================================
Attribute Description
========= ====================================================================
dp A ryu.controller.controller.Datapath instance of the switch
enter True when the switch connected to our controller. False for
disconnect.
========= ====================================================================
ryu.controller.dpset.EventPortAdd
=================================
.. autoclass:: ryu.controller.dpset.EventPortAdd
An event class for switch port status notification.
This event is generated when a new port is added to a switch.
For OpenFlow switches, one can get the same notification by observing
ryu.controller.ofp_event.EventOFPPortStatus.
An instance has at least the following attributes.
========= ====================================================================
Attribute Description
========= ====================================================================
dp A ryu.controller.controller.Datapath instance of the switch
port port number
========= ====================================================================
ryu.controller.dpset.EventPortDelete
====================================
.. autoclass:: ryu.controller.dpset.EventPortDelete
An event class for switch port status notification.
This event is generated when a port is removed from a switch.
For OpenFlow switches, one can get the same notification by observing
ryu.controller.ofp_event.EventOFPPortStatus.
An instance has at least the following attributes.
========= ====================================================================
Attribute Description
========= ====================================================================
dp A ryu.controller.controller.Datapath instance of the switch
port port number
========= ====================================================================
ryu.controller.dpset.EventPortModify
====================================
.. autoclass:: ryu.controller.dpset.EventPortModify
An event class for switch port status notification.
This event is generated when some attribute of a port is changed.
For OpenFlow switches, one can get the same notification by observing
ryu.controller.ofp_event.EventOFPPortStatus.
An instance has at least the following attributes.
========= ====================================================================
Attribute Description
========= ====================================================================
dp A ryu.controller.controller.Datapath instance of the switch
port port number
========= ====================================================================
ryu.controller.network.EventNetworkPort
=======================================
.. autoclass:: ryu.controller.network.EventNetworkPort
An event class for notification of port arrival and deperture.
This event is generated when a port is introduced to or removed from a network
by the REST API.
An instance has at least the following attributes.
========== ===================================================================
Attribute Description
========== ===================================================================
network_id Network ID
dpid OpenFlow Datapath ID of the switch to which the port belongs.
port_no OpenFlow port number of the port
add_del True for adding a port. False for removing a port.
========== ===================================================================
ryu.controller.network.EventNetworkDel
======================================
.. autoclass:: ryu.controller.network.EventNetworkDel
An event class for network deletion.
This event is generated when a network is deleted by the REST API.
An instance has at least the following attributes.
========== ===================================================================
Attribute Description
========== ===================================================================
network_id Network ID
========== ===================================================================
ryu.controller.network.EventMacAddress
======================================
.. autoclass:: ryu.controller.network.EventMacAddress
An event class for end-point MAC address registration.
This event is generated when a end-point MAC address is updated
by the REST API.
An instance has at least the following attributes.
=========== ==================================================================
Attribute Description
=========== ==================================================================
network_id Network ID
dpid OpenFlow Datapath ID of the switch to which the port belongs.
port_no OpenFlow port number of the port
mac_address The old MAC address of the port if add_del is False. Otherwise
the new MAC address.
add_del False if this event is a result of a port removal. Otherwise
True.
=========== ==================================================================
ryu.controller.tunnels.EventTunnelKeyAdd
========================================
.. autoclass:: ryu.controller.tunnels.EventTunnelKeyAdd
An event class for tunnel key registration.
This event is generated when a tunnel key is registered or updated
by the REST API.
An instance has at least the following attributes.
=========== ==================================================================
Attribute Description
=========== ==================================================================
network_id Network ID
tunnel_key Tunnel Key
=========== ==================================================================
ryu.controller.tunnels.EventTunnelKeyDel
========================================
.. autoclass:: ryu.controller.tunnels.EventTunnelKeyDel
An event class for tunnel key registration.
This event is generated when a tunnel key is removed by the REST API.
An instance has at least the following attributes.
=========== ==================================================================
Attribute Description
=========== ==================================================================
network_id Network ID
tunnel_key Tunnel Key
=========== ==================================================================
ryu.controller.tunnels.EventTunnelPort
======================================
.. autoclass:: ryu.controller.tunnels.EventTunnelPort
An event class for tunnel port registration.
This event is generated when a tunnel port is added or removed by the REST API.
An instance has at least the following attributes.
=========== ==================================================================
Attribute Description
=========== ==================================================================
dpid OpenFlow Datapath ID
port_no OpenFlow port number
remote_dpid OpenFlow port number of the tunnel peer
add_del True for adding a tunnel. False for removal.
=========== ==================================================================

View File

@ -19,7 +19,7 @@ The test procedure
* run LINC switch
* run Ryu test_of_config app
For getting/installing Ryu itself, please refer to https://ryu-sdn.org/
For getting/installing Ryu itself, please refer to http://osrg.github.io/ryu/
Install Erlang environment

View File

@ -4,17 +4,10 @@
Using Ryu Network Operating System with OpenStack as OpenFlow controller
************************************************************************
.. CAUTION::
The Ryu plugin and OFAgent described in the following is deprecated,
because Ryu is officially integrated into Open vSwitch agent with
"of_interface = native" mode.
Ryu cooperates with OpenStack using Quantum Ryu plugin. The plugin is
available in the official Quantum releases.
For more information, please visit https://github.com/faucetsdn/ryu/wiki/OpenStack .
For more information, please visit http://github.com/osrg/ryu/wiki/OpenStack .
We described instructions of the installation / configuration of OpenStack
with Ryu, and we provide pre-configured VM image to be able to easily try
OpenStack with Ryu.

View File

@ -5,21 +5,21 @@ The First Application
Whetting Your Appetite
======================
If you want to manage network gear (switches, routers, etc) your
own way, you just need to write your own Ryu application. Your application
tells Ryu how you want to manage the gear. Then Ryu configures the
gear by using OpenFlow protocol, etc.
If you want to manage the network gears (switches, routers, etc) at
your way, you need to write your Ryu application. Your application
tells Ryu how you want to manage the gears. Then Ryu configures the
gears by using OpenFlow protocol, etc.
Writing Ryu applications is easy. They're just Python scripts.
Writing Ryu application is easy. It's just Python scripts.
Start Writing
=============
Here we show a Ryu application that makes an OpenFlow switch work as a dumb
We show a Ryu application that make OpenFlow switches work as a dumb
layer 2 switch.
Open a text editor and create a new file with the following content:
Open a text editor creating a new file with the following content:
.. code-block:: python
@ -29,9 +29,9 @@ Open a text editor and create a new file with the following content:
def __init__(self, *args, **kwargs):
super(L2Switch, self).__init__(*args, **kwargs)
Ryu applications are just Python scripts so you can save the file with
any name, any extension, and any place you want. Let's name the file
'l2.py' in your home directory.
Ryu application is just a Python script so you can save the file with
any name, extensions, and any place you want. Let's name the file
'l2.py' at your home directory.
This application does nothing useful yet, however it's a complete Ryu
application. In fact, you can run this Ryu application::
@ -41,10 +41,10 @@ application. In fact, you can run this Ryu application::
instantiating app /Users/fujita/l2.py
All you have to do is define a new subclass of RyuApp to run
All you have to do is defining needs a new subclass of RyuApp to run
your Python script as a Ryu application.
Next let's add some functionality that sends a received packet to all
Next let's add the functionality of sending a received packet to all
the ports.
.. code-block:: python
@ -53,11 +53,8 @@ the ports.
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_0
class L2Switch(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(L2Switch, self).__init__(*args, **kwargs)
@ -67,31 +64,26 @@ the ports.
dp = msg.datapath
ofp = dp.ofproto
ofp_parser = dp.ofproto_parser
actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
data = None
if msg.buffer_id == ofp.OFP_NO_BUFFER:
data = msg.data
out = ofp_parser.OFPPacketOut(
datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port,
actions=actions, data = data)
actions=actions)
dp.send_msg(out)
A new method 'packet_in_handler' is added to the L2Switch class. This is
called when Ryu receives an OpenFlow packet_in message. The trick is the
A new method 'packet_in_handler' is added to L2Switch class. This is
called when Ryu receives an OpenFlow packet_in message. The trick is
'set_ev_cls' decorator. This decorator tells Ryu when the decorated
function should be called.
The first argument of the decorator indicates which type of event this
function should be called for. As you might expect, every time Ryu gets a
The first argument of the decorator indicates an event that makes
function called. As you expect easily, every time Ryu gets a
packet_in message, this function is called.
The second argument indicates the state of the switch. You probably
The second argument indicates the state of the switch. Probably, you
want to ignore packet_in messages before the negotiation between Ryu
and the switch is finished. Using 'MAIN_DISPATCHER' as the second
and the switch finishes. Using 'MAIN_DISPATCHER' as the second
argument means this function is called only after the negotiation
completes.
@ -108,24 +100,24 @@ Ready for the second half.
* OFPActionOutput class is used with a packet_out message to specify a
switch port that you want to send the packet out of. This
application uses the OFPP_FLOOD flag to indicate that the packet should
be sent out on all ports.
application need a switch to send out of all the ports so OFPP_FLOOD
constant is used.
* OFPPacketOut class is used to build a packet_out message.
* If you call Datapath class's send_msg method with a OpenFlow message
class object, Ryu builds and sends the on-wire data format to the switch.
class object, Ryu builds and send the on-wire data format to the switch.
There, you finished implementing your first Ryu application. You are ready to
run a Ryu application that does something useful.
Here, you finished implementing your first Ryu application. You are ready to
run this Ryu application that does something useful.
Is a dumb L2 switch is too dumb? You want to implement a learning L2
A dumb l2 switch is too dumb? You want to implement a learning l2
switch? Move to `the next step
<https://github.com/faucetsdn/ryu/blob/master/ryu/app/simple_switch.py>`_. You
<https://github.com/osrg/ryu/blob/master/ryu/app/simple_switch.py>`_. You
can learn from the existing Ryu applications at `ryu/app
<https://github.com/faucetsdn/ryu/blob/master/ryu/app/>`_ directory and
<https://github.com/osrg/ryu/blob/master/ryu/app/>`_ directory and
`integrated tests
<https://github.com/faucetsdn/ryu/blob/master/ryu/tests/integrated/>`_
<https://github.com/osrg/ryu/blob/master/ryu/tests/integrated/>`_
directory.

View File

@ -1 +0,0 @@
pip==20.3.4

View File

@ -8,16 +8,16 @@ usage() {
echo "Usage: $0 [OPTION]..."
echo "Run Ryu's test suite(s)"
echo ""
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
echo " -c, --coverage Generate coverage report"
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
echo " -p, --pycodestyle, --pep8 Just run pycodestyle(pep8)"
echo " -P, --no-pycodestyle, --no-pep8 Don't run pycodestyle(pep8)"
echo " -l, --pylint Just run pylint"
echo " -i, --integrated Run integrated test"
echo " -v, --verbose Run verbose pylint analysis"
echo " -h, --help Print this usage message"
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
echo " -c, --coverage Generate coverage report"
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
echo " -p, --pep8 Just run pep8"
echo " -P, --no-pep8 Don't run pep8"
echo " -l, --pylint Just run pylint"
echo " -i, --integrated Run integrated test"
echo " -v, --verbose Run verbose pylint analysis"
echo " -h, --help Print this usage message"
echo ""
echo "Note: with no options specified, the script will try to run the tests in a virtual environment,"
echo " If no virtualenv is found, the script will ask if you would like to create one. If you "
@ -31,8 +31,8 @@ process_option() {
-V|--virtual-env) always_venv=1; never_venv=0;;
-N|--no-virtual-env) always_venv=0; never_venv=1;;
-f|--force) force=1;;
-p|--pycodestyle|--pep8) just_pycodestyle=1; never_venv=1; always_venv=0;;
-P|--no-pycodestyle|--no-pep8) no_pycodestyle=1;;
-p|--pep8) just_pep8=1; never_venv=1; always_venv=0;;
-P|--no-pep8) no_pep8=1;;
-l|--pylint) just_pylint=1;;
-i|--integrated) integrated=1;;
-c|--coverage) coverage=1;;
@ -46,8 +46,8 @@ venv=.venv
with_venv=tools/with_venv.sh
always_venv=0
never_venv=0
just_pycodestyle=0
no_pycodestyle=0
just_pep8=0
no_pep8=0
just_pylint=0
integrated=0
force=0
@ -103,26 +103,20 @@ run_pylint() {
export PYTHONPATH=$OLD_PYTHONPATH
}
run_pycodestyle() {
PYCODESTYLE=$(which pycodestyle || which pep8)
if [ -z "${PYCODESTYLE}" ]
then
echo "Please install pycodestyle or pep8"
return 1
fi
echo "Running $(basename ${PYCODESTYLE}) ..."
run_pep8() {
echo "Running pep8 ..."
PYCODESTYLE_OPTIONS="--repeat --show-source"
PYCODESTYLE_INCLUDE="ryu setup*.py"
PYCODESTYLE_LOG=pycodestyle.log
${wrapper} ${PYCODESTYLE} $PYCODESTYLE_OPTIONS $PYCODESTYLE_INCLUDE | tee $PYCODESTYLE_LOG
PEP8_OPTIONS="--repeat --show-source"
PEP8_INCLUDE="ryu setup*.py"
PEP8_LOG=pep8.log
${wrapper} pep8 $PEP8_OPTIONS $PEP8_INCLUDE | tee $PEP8_LOG
}
run_integrated() {
echo "Running integrated test ..."
INTEGRATED_TEST_RUNNER="./ryu/tests/integrated/run_tests_with_ovs12.py"
sudo PYTHONPATH=. nosetests -s $INTEGRATED_TEST_RUNNER
sudo PYTHONPATH=. nosetests -s $INTEGRATED_TEST_RUNNER
}
#NOSETESTS="nosetests $noseopts $noseargs"
NOSETESTS="${PYTHON} ./ryu/tests/run_tests.py $noseopts $noseargs"
@ -167,8 +161,8 @@ if [ $coverage -eq 1 ]; then
${wrapper} coverage erase
fi
if [ $just_pycodestyle -eq 1 ]; then
run_pycodestyle
if [ $just_pep8 -eq 1 ]; then
run_pep8
exit
fi
if [ $just_pylint -eq 1 ]; then
@ -183,8 +177,8 @@ fi
run_tests
RV=$?
if [ $no_pycodestyle -eq 0 ]; then
run_pycodestyle
if [ $no_pep8 -eq 0 ]; then
run_pep8
fi
if [ $coverage -eq 1 ]; then

View File

@ -14,5 +14,5 @@
# limitations under the License.
version_info = (4, 34)
version_info = (3, 25)
version = '.'.join(map(str, version_info))

286
ryu/app/client.py Normal file
View File

@ -0,0 +1,286 @@
# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2011, 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.
# This is a client library for Ryu REST API. (ryu.app.rest_quantum etc)
# This module is *not* used by ryu-manager.
# Imported and used by OpenStack Ryu plug-in and agent.
from six.moves import http_client
import json
from six.moves import urllib_parse
def ignore_http_not_found(func):
"""
Ignore http not found(404) with Ryu client library.
Ryu client raises httplib.HTTPException with an error in args[0]
"""
try:
func()
except http_client.HTTPException as e:
res = e.args[0]
if res.status != http_client.NOT_FOUND:
raise
class RyuClientBase(object):
def __init__(self, version, address):
super(RyuClientBase, self).__init__()
self.version = version
res = urllib_parse.SplitResult('', address, '', '', '')
self.host = res.hostname
self.port = res.port
self.url_prefix = '/' + self.version + '/'
def _do_request(self, method, action, body=None):
conn = http_client.HTTPConnection(self.host, self.port)
url = self.url_prefix + action
headers = {}
if body is not None:
body = json.dumps(body)
headers['Content-Type'] = 'application/json'
conn.request(method, url, body, headers)
res = conn.getresponse()
if res.status in (http_client.OK,
http_client.CREATED,
http_client.ACCEPTED,
http_client.NO_CONTENT):
return res
raise http_client.HTTPException(
res, 'code %d reason %s' % (res.status, res.reason),
res.getheaders(), res.read())
def _do_request_read(self, method, action):
res = self._do_request(method, action)
return res.read()
class OFPClientV1_0(RyuClientBase):
version = 'v1.0'
# /networks/{network_id}/{dpid}_{port}/macs/{mac_address}
path_networks = 'networks'
path_network = path_networks + '/%s'
path_port = path_network + '/%s_%s'
path_macs = path_port + '/macs'
path_mac = path_macs + '/%s'
def __init__(self, address):
super(OFPClientV1_0, self).__init__(OFPClientV1_0.version, address)
def get_networks(self):
return self._do_request_read('GET', self.path_networks)
def create_network(self, network_id):
self._do_request('POST', self.path_network % network_id)
def update_network(self, network_id):
self._do_request('PUT', self.path_network % network_id)
def delete_network(self, network_id):
self._do_request('DELETE', self.path_network % network_id)
def get_ports(self, network_id):
return self._do_request_read('GET', self.path_network % network_id)
def create_port(self, network_id, dpid, port):
self._do_request('POST', self.path_port % (network_id, dpid, port))
def update_port(self, network_id, dpid, port):
self._do_request('PUT', self.path_port % (network_id, dpid, port))
def delete_port(self, network_id, dpid, port):
self._do_request('DELETE', self.path_port % (network_id, dpid, port))
def list_macs(self, network_id, dpid, port):
return self._do_request_read('GET',
self.path_macs % (network_id, dpid, port))
def create_mac(self, network_id, dpid, port, mac_address):
self._do_request('POST', self.path_mac % (network_id, dpid, port,
mac_address))
def update_mac(self, network_id, dpid, port, mac_address):
self._do_request('PUT', self.path_mac % (network_id, dpid, port,
mac_address))
OFPClient = OFPClientV1_0
class TunnelClientV1_0(RyuClientBase):
version = 'v1.0'
# /tunnels/networks/{network-id}/key/{tunnel_key}
# /tunnels/switches/{dpid}/ports/{port-id}/{remote_dpip}
path_tunnels = 'tunnels'
path_key = path_tunnels + '/networks/%(network_id)s/key'
path_tunnel_key = path_key + '/%(tunnel_key)s'
path_ports = path_tunnels + '/switches/%(dpid)s/ports'
path_port = path_ports + '/%(port_no)s'
path_remote_dpid = path_port + '/%(remote_dpid)s'
def __init__(self, address):
super(TunnelClientV1_0, self).__init__(self.version, address)
def get_tunnel_key(self, network_id):
return self._do_request_read('GET', self.path_key % locals())
def delete_tunnel_key(self, network_id):
return self._do_request_read('DELETE', self.path_key % locals())
def create_tunnel_key(self, network_id, tunnel_key):
self._do_request('POST', self.path_tunnel_key % locals())
def update_tunnel_key(self, network_id, tunnel_key):
self._do_request('PUT', self.path_tunnel_key % locals())
def list_ports(self, dpid):
return self._do_request_read('GET', self.path_ports % locals())
def delete_port(self, dpid, port_no):
return self._do_request_read('DELETE', self.path_port % locals())
def get_remote_dpid(self, dpid, port_no):
return self._do_request_read('GET', self.path_port % locals())
def create_remote_dpid(self, dpid, port_no, remote_dpid):
self._do_request('POST', self.path_remote_dpid % locals())
def update_remote_dpid(self, dpid, port_no, remote_dpid):
self._do_request('PUT', self.path_remote_dpid % locals())
TunnelClient = TunnelClientV1_0
class SwitchConfClientV1_0(RyuClientBase):
version = 'v1.0'
# /conf/switches
# /conf/switches/<dpid>
# /conf/switches/<dpid>/<key>
path_conf_switches = 'conf/switches'
path_switch = path_conf_switches + '/%(dpid)s'
path_key = path_switch + '/%(key)s'
def __init__(self, address):
super(SwitchConfClientV1_0, self).__init__(self.version, address)
def list_switches(self):
return self._do_request_read('GET', self.path_conf_switches)
def delete_switch(self, dpid):
self._do_request('DELETE', self.path_switch % locals())
def list_keys(self, dpid):
return self._do_request_read('GET', self.path_switch % locals())
def set_key(self, dpid, key, value):
self._do_request('PUT', self.path_key % locals(), value)
def get_key(self, dpid, key):
return self._do_request_read('GET', self.path_key % locals())
def delete_key(self, dpid, key):
self._do_request('DELETE', self.path_key % locals())
SwitchConfClient = SwitchConfClientV1_0
class QuantumIfaceClientV1_0(RyuClientBase):
version = 'v1.0'
# /quantum/ports
# /quantum/ports/{iface_id}
# /quantum/ports/{iface_id}/keys/
# /quantum/ports/{iface_id}/keys/{key}/{value}
path_quantum_ports = 'quantum/ports'
path_iface_id = path_quantum_ports + '/%(iface_id)s'
path_keys = path_iface_id + '/keys'
path_key = path_keys + '/%(key)s'
path_value = path_key + '/%(value)s'
def __init__(self, address):
super(QuantumIfaceClientV1_0, self).__init__(self.version, address)
def list_ifaces(self):
return self._do_request_read('GET', self.path_quantum_ports)
def delete_iface(self, iface_id):
self._do_request('DELETE', self.path_iface_id % locals())
def list_keys(self, iface_id):
return self._do_request_read('GET', self.path_keys % locals())
def get_key(self, iface_id, key):
return self._do_request_read('GET', self.path_key % locals())
def create_key(self, iface_id, key, value):
self._do_request('POST', self.path_value % locals())
def update_key(self, iface_id, key, value):
self._do_request('PUT', self.path_value % locals())
# for convenience
def get_network_id(self, iface_id):
return self.get_key(iface_id, 'network_id')
def create_network_id(self, iface_id, network_id):
self.create_key(iface_id, 'network_id', network_id)
def update_network_id(self, iface_id, network_id):
self.update_key(iface_id, 'network_id', network_id)
QuantumIfaceClient = QuantumIfaceClientV1_0
NeutronIfaceClient = QuantumIfaceClient # project rename quantum -> neutron
class TopologyClientV1_0(RyuClientBase):
version = 'v1.0'
# /topology/switches
# /topology/switches/{dpid}
# /topology/links
# /topology/links/{dpid}
_path_switches = 'topology/switches'
_path_links = 'topology/links'
def __init__(self, address):
super(TopologyClientV1_0, self).__init__(self.version, address)
# dpid: string representation (see ryu.lib.dpid)
# if None, get all
def list_switches(self, dpid=None):
uri = self._path_switches
if dpid:
uri += '/%s' % (dpid)
return self._do_request('GET', uri)
# dpid: string representation (see ryu.lib.dpid)
# if None, get all
def list_links(self, dpid=None):
uri = self._path_links
if dpid:
uri += '/%s' % (dpid)
return self._do_request('GET', uri)
TopologyClient = TopologyClientV1_0

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 ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
class ExampleSwitch13(app_manager.RyuApp):
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)

980
ryu/app/gre_tunnel.py Normal file
View File

@ -0,0 +1,980 @@
# 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 updates flow table for OpenStack integration.
# Despite of the name, this module isn't GRE specific and
# should work for VXLAN etc as well.
"""
Flow table updater for OpenStack integration. Despite of the name, this
isn't GRE specific.
"""
import collections
from ryu import exception as ryu_exc
from ryu.app.rest_nw_id import (NW_ID_VPORT_GRE,
RESERVED_NETWORK_IDS)
from ryu.base import app_manager
from ryu.controller import (dpset,
event,
handler,
network,
ofp_event,
tunnels)
from ryu.ofproto import nx_match
from ryu.lib import dpid as dpid_lib
from ryu.lib import mac
def _is_reserved_port(ofproto, port_no):
return port_no > ofproto.OFPP_MAX
def _link_is_up(dpset_, dp, port_no):
try:
state = dpset_.get_port(dp.id, port_no).state
return not (state & dp.ofproto.OFPPS_LINK_DOWN)
except ryu_exc.PortNotFound:
return False
class PortSet(app_manager.RyuApp):
# Those events are higher level events than events of network tenant,
# tunnel ports as the race conditions are masked.
# Add event is generated only when all necessary informations are gathered,
# Del event is generated when any one of the informations are deleted.
#
# Example: ports for VMs
# there is a race condition between ofp port add/del event and
# register network_id for the port.
class EventTunnelKeyDel(event.EventBase):
def __init__(self, tunnel_key):
super(PortSet.EventTunnelKeyDel, self).__init__()
self.tunnel_key = tunnel_key
class EventPortBase(event.EventBase):
def __init__(self, dpid, port_no):
super(PortSet.EventPortBase, self).__init__()
self.dpid = dpid
self.port_no = port_no
class EventVMPort(EventPortBase):
def __init__(self, network_id, tunnel_key,
dpid, port_no, mac_address, add_del):
super(PortSet.EventVMPort, self).__init__(dpid, port_no)
self.network_id = network_id
self.tunnel_key = tunnel_key
self.mac_address = mac_address
self.add_del = add_del
def __str__(self):
return ('EventVMPort<dpid %s port_no %d '
'network_id %s tunnel_key %s mac %s add_del %s>' %
(dpid_lib.dpid_to_str(self.dpid), self.port_no,
self.network_id, self.tunnel_key,
mac.haddr_to_str(self.mac_address), self.add_del))
class EventTunnelPort(EventPortBase):
def __init__(self, dpid, port_no, remote_dpid, add_del):
super(PortSet.EventTunnelPort, self).__init__(dpid, port_no)
self.remote_dpid = remote_dpid
self.add_del = add_del
def __str__(self):
return ('EventTunnelPort<dpid %s port_no %d remote_dpid %s '
'add_del %s>' %
(dpid_lib.dpid_to_str(self.dpid), self.port_no,
dpid_lib.dpid_to_str(self.remote_dpid), self.add_del))
def __init__(self, **kwargs):
super(PortSet, self).__init__()
self.nw = kwargs['network']
self.tunnels = kwargs['tunnels']
self.dpset = kwargs['dpset']
app_manager.register_app(self)
def _check_link_state(self, dp, port_no, add_del):
if add_del:
# When adding port, the link should be UP.
return _link_is_up(self.dpset, dp, port_no)
else:
# When deleting port, the link status isn't cared.
return True
# Tunnel port
# of connecting: self.dpids by (dpid, port_no)
# datapath: connected: EventDP event
# port status: UP: port add/delete/modify event
# remote dpid: self.tunnels by (dpid, port_no): tunnel port add/del even
def _tunnel_port_handler(self, dpid, port_no, add_del):
dp = self.dpset.get(dpid)
if dp is None:
return
if not self._check_link_state(dp, port_no, add_del):
return
try:
remote_dpid = self.tunnels.get_remote_dpid(dpid, port_no)
except ryu_exc.PortNotFound:
return
self.send_event_to_observers(self.EventTunnelPort(dpid, port_no,
remote_dpid, add_del))
# VM port
# of connection: self.dpids by (dpid, port_no)
# datapath: connected: EventDP event
# port status: UP: Port add/delete/modify event
# network_id: self.nw by (dpid, port_no): network port add/del event
# mac_address: self.nw by (dpid, port_no): mac address add/del event
# tunnel key: from self.tunnels by network_id: tunnel key add/del event
def _vm_port_handler(self, dpid, port_no,
network_id, mac_address, add_del):
if network_id in RESERVED_NETWORK_IDS:
return
if mac_address is None:
return
dp = self.dpset.get(dpid)
if dp is None:
return
if _is_reserved_port(dp.ofproto, port_no):
return
if not self._check_link_state(dp, port_no, add_del):
return
try:
tunnel_key = self.tunnels.get_key(network_id)
except tunnels.TunnelKeyNotFound:
return
self.send_event_to_observers(self.EventVMPort(network_id, tunnel_key,
dpid, port_no, mac_address, add_del))
def _vm_port_mac_handler(self, dpid, port_no, network_id, add_del):
if network_id == NW_ID_VPORT_GRE:
self._tunnel_port_handler(dpid, port_no, add_del)
return
try:
mac_address = self.nw.get_mac(dpid, port_no)
except ryu_exc.PortNotFound:
return
self._vm_port_handler(dpid, port_no, network_id, mac_address,
add_del)
def _port_handler(self, dpid, port_no, add_del):
"""
:type add_del: bool
:param add_del: True for add, False for del
"""
try:
port = self.nw.get_port(dpid, port_no)
except ryu_exc.PortNotFound:
return
if port.network_id is None:
return
if port.network_id == NW_ID_VPORT_GRE:
self._tunnel_port_handler(dpid, port_no, add_del)
return
self._vm_port_handler(dpid, port_no, port.network_id,
port.mac_address, add_del)
def _tunnel_key_del(self, tunnel_key):
self.send_event_to_observers(self.EventTunnelKeyDel(tunnel_key))
# nw: network del
# port add/del (vm/tunnel port)
# mac address add/del(only vm port)
# tunnels: tunnel key add/del
# tunnel port add/del
# dpset: eventdp
# port add/delete/modify
@handler.set_ev_cls(network.EventNetworkDel)
def network_del_handler(self, ev):
network_id = ev.network_id
if network_id in RESERVED_NETWORK_IDS:
return
try:
tunnel_key = self.tunnels.get_key(network_id)
except tunnels.TunnelKeyNotFound:
return
self._tunnel_key_del(tunnel_key)
@handler.set_ev_cls(network.EventNetworkPort)
def network_port_handler(self, ev):
self._vm_port_mac_handler(ev.dpid, ev.port_no, ev.network_id,
ev.add_del)
@handler.set_ev_cls(network.EventMacAddress)
def network_mac_address_handler(self, ev):
self._vm_port_handler(ev.dpid, ev.port_no, ev.network_id,
ev.mac_address, ev.add_del)
@handler.set_ev_cls(tunnels.EventTunnelKeyAdd)
def tunnel_key_add_handler(self, ev):
network_id = ev.network_id
for (dpid, port_no) in self.nw.list_ports_noraise(network_id):
self._vm_port_mac_handler(dpid, port_no, network_id, True)
@handler.set_ev_cls(tunnels.EventTunnelKeyDel)
def tunnel_key_del_handler(self, ev):
network_id = ev.network_id
for (dpid, port_no) in self.nw.list_ports_noraise(network_id):
self._vm_port_mac_handler(dpid, port_no, network_id, False)
if self.nw.has_network(network_id):
self._tunnel_key_del(ev.tunnel_key)
@handler.set_ev_cls(tunnels.EventTunnelPort)
def tunnel_port_handler(self, ev):
self._port_handler(ev.dpid, ev.port_no, ev.add_del)
@handler.set_ev_cls(dpset.EventDP)
def dp_handler(self, ev):
self.send_event_to_observers(ev)
enter_leave = ev.enter
if not enter_leave:
# TODO:XXX
# What to do on datapath disconnection?
self.logger.debug('dp disconnection ev:%s', ev)
dpid = ev.dp.id
ports = set(port.port_no for port in ev.ports)
ports.update(port.port_no for port in self.nw.get_ports(dpid))
for port_no in ports:
self._port_handler(dpid, port_no, enter_leave)
@handler.set_ev_cls(dpset.EventPortAdd)
def port_add_handler(self, ev):
self._port_handler(ev.dp.id, ev.port.port_no, True)
@handler.set_ev_cls(dpset.EventPortDelete)
def port_del_handler(self, ev):
self._port_handler(ev.dp.id, ev.port.port_no, False)
@handler.set_ev_cls(dpset.EventPortModify)
def port_modify_handler(self, ev):
# We don't know LINK status has been changed.
# So VM/TUNNEL port event can be triggered many times.
dp = ev.dp
port = ev.port
self._port_handler(dp.id, port.port_no,
not (port.state & dp.ofproto.OFPPS_LINK_DOWN))
@handler.set_ev_cls(ofp_event.EventOFPPacketIn)
def packet_in_handler(self, ev):
# for debug
self.send_event_to_observers(ev)
def cls_rule(in_port=None, tun_id=None, dl_src=None, dl_dst=None):
"""Convenience function to initialize nx_match.ClsRule()"""
rule = nx_match.ClsRule()
if in_port is not None:
rule.set_in_port(in_port)
if tun_id is not None:
rule.set_tun_id(tun_id)
if dl_src is not None:
rule.set_dl_src(dl_src)
if dl_dst is not None:
rule.set_dl_dst(dl_dst)
return rule
class GRETunnel(app_manager.RyuApp):
"""
app for L2/L3 with gre tunneling
PORTS
VM-port: the port which is connected to VM instance
TUNNEL-port: the ovs GRE vport
TABLES: multi tables is used
SRC_TABLE:
This table is firstly used to match packets.
by in_port, determine which port the packet comes VM-port or
TUNNEL-port.
If the packet came from VM-port, set tunnel id based on which network
the VM belongs to, and send the packet to the tunnel out table.
If the packet came from TUNNEL-port and its tunnel id is known to this
switch, send the packet to local out table. Otherwise drop it.
TUNNEL_OUT_TABLE:
This table looks at tunnel id and dl_dst, send the packet to tunnel
ports if necessary. And then, sends the packet to LOCAL_OUT_TABLE.
By matching the packet with tunnel_id and dl_dst, determine which
tunnel port the packet is send to.
LOCAL_OUT_TABLE:
This table looks at tunnel id and dl_dst, send the packet to local
VM ports if necessary. Otherwise drop the packet.
The packet from vm port traverses as
SRC_TABLE -> TUNNEL_OUT_TABLE -> LOCAL_OUT_TABLE
The packet from tunnel port traverses as
SRC_TABLE -> LOCAL_OUT_TABLE
The packet from vm port:
SRC_TABLE
match action
in_port(VM) & dl_src set_tunnel & goto TUNNEL_OUT_TABLE
in_port(VM) drop (catch-all drop rule)
in_port(TUNNEL) & tun_id goto LOCAL_OUT_TABLE
in_port(TUNNEL) drop (catch-all drop rule)
TUNNEL_OUT_TABLE
match action
tun_id & dl_dst out tunnel port & goto LOCAL_OUT_TABLE
(unicast or broadcast)
tun_id goto LOCAL_OUT_TABLE (catch-all rule)
LOCAL_OUT_TABLE
tun_id & dl_dst output(VM) (unicast or broadcast)
tun_id drop (catch-all drop rule)
NOTE:
adding/deleting flow entries should be done carefully in certain order
such that packet in event should not be triggered.
"""
_CONTEXTS = {
'network': network.Network,
'dpset': dpset.DPSet,
'tunnels': tunnels.Tunnels,
}
DEFAULT_COOKIE = 0 # cookie isn't used. Just set 0
# Tables
SRC_TABLE = 0
TUNNEL_OUT_TABLE = 1
LOCAL_OUT_TABLE = 2
FLOW_TABLES = [SRC_TABLE, TUNNEL_OUT_TABLE, LOCAL_OUT_TABLE]
# Priorities. The only inequality is important.
# '/ 2' is used just for easy looking instead of '- 1'.
# 0x7ffff vs 0x4000
TABLE_DEFAULT_PRPIRITY = 32768 # = ofproto.OFP_DEFAULT_PRIORITY
# SRC_TABLE for VM-port
SRC_PRI_MAC = TABLE_DEFAULT_PRPIRITY
SRC_PRI_DROP = TABLE_DEFAULT_PRPIRITY / 2
# SRC_TABLE for TUNNEL-port
SRC_PRI_TUNNEL_PASS = TABLE_DEFAULT_PRPIRITY
SRC_PRI_TUNNEL_DROP = TABLE_DEFAULT_PRPIRITY / 2
# TUNNEL_OUT_TABLE
TUNNEL_OUT_PRI_MAC = TABLE_DEFAULT_PRPIRITY
TUNNEL_OUT_PRI_BROADCAST = TABLE_DEFAULT_PRPIRITY / 2
TUNNEL_OUT_PRI_PASS = TABLE_DEFAULT_PRPIRITY / 4
TUNNEL_OUT_PRI_DROP = TABLE_DEFAULT_PRPIRITY / 8
# LOCAL_OUT_TABLE
LOCAL_OUT_PRI_MAC = TABLE_DEFAULT_PRPIRITY
LOCAL_OUT_PRI_BROADCAST = TABLE_DEFAULT_PRPIRITY / 2
LOCAL_OUT_PRI_DROP = TABLE_DEFAULT_PRPIRITY / 4
def __init__(self, *args, **kwargs):
super(GRETunnel, self).__init__(*args, **kwargs)
self.nw = kwargs['network']
self.dpset = kwargs['dpset']
self.tunnels = kwargs['tunnels']
self.port_set = PortSet(**kwargs)
map(lambda ev_cls: self.port_set.register_observer(ev_cls, self.name),
[dpset.EventDP, PortSet.EventTunnelKeyDel, PortSet.EventVMPort,
PortSet.EventTunnelPort, ofp_event.EventOFPPacketIn])
def start(self):
super(GRETunnel, self).start()
self.port_set.start()
def stop(self):
app_mgr = app_manager.get_instance()
app_mgr.uninstantiate(self.port_set)
self.port_set = None
super(GRETunnel, self).stop()
# TODO: track active vm/tunnel ports
@handler.set_ev_handler(dpset.EventDP)
def dp_handler(self, ev):
if not ev.enter:
return
# enable nicira extension
# TODO:XXX error handling
dp = ev.dp
ofproto = dp.ofproto
dp.send_nxt_set_flow_format(ofproto.NXFF_NXM)
flow_mod_table_id = dp.ofproto_parser.NXTFlowModTableId(dp, 1)
dp.send_msg(flow_mod_table_id)
dp.send_barrier()
# delete all flows in all tables
# current controller.handlers takes care of only table = 0
for table in self.FLOW_TABLES:
rule = cls_rule()
self.send_flow_del(dp, rule, table, ofproto.OFPFC_DELETE,
None, None)
dp.send_barrier()
@staticmethod
def _make_command(table, command):
return table << 8 | command
def send_flow_mod(self, dp, rule, table, command, priority, actions):
command = self._make_command(table, command)
dp.send_flow_mod(rule=rule, cookie=self.DEFAULT_COOKIE,
command=command, idle_timeout=0,
hard_timeout=0, priority=priority, actions=actions)
def send_flow_del(self, dp, rule, table, command, priority, out_port):
command = self._make_command(table, command)
dp.send_flow_mod(rule=rule, cookie=self.DEFAULT_COOKIE,
command=command, idle_timeout=0,
hard_timeout=0, priority=priority, out_port=out_port)
def _list_tunnel_port(self, dp, remote_dpids):
dpid = dp.id
tunnel_ports = []
for other_dpid in remote_dpids:
if other_dpid == dpid:
continue
other_dp = self.dpset.get(other_dpid)
if other_dp is None:
continue
try:
port_no = self.tunnels.get_port(dpid, other_dpid)
except ryu_exc.PortNotFound:
continue
if not self._link_is_up(dp, port_no):
continue
tunnel_ports.append(port_no)
return tunnel_ports
def _link_is_up(self, dp, port_no):
return _link_is_up(self.dpset, dp, port_no)
def _port_is_active(self, network_id, dp, nw_port):
return (nw_port.network_id == network_id and
nw_port.mac_address is not None and
self._link_is_up(dp, nw_port.port_no))
def _tunnel_port_with_mac(self, remote_dp, dpid, network_id, port_no,
mac_address):
tunnel_ports = []
ports = self.nw.get_ports_with_mac(network_id, mac_address).copy()
ports.discard((dpid, port_no))
assert len(ports) <= 1
for port in ports:
try:
tunnel_port_no = self.tunnels.get_port(remote_dp.id, port.dpid)
except ryu_exc.PortNotFound:
pass
else:
if self._link_is_up(remote_dp, tunnel_port_no):
tunnel_ports.append(tunnel_port_no)
assert len(tunnel_ports) <= 1
return tunnel_ports
def _vm_port_add(self, ev):
dpid = ev.dpid
dp = self.dpset.get(dpid)
assert dp is not None
ofproto = dp.ofproto
ofproto_parser = dp.ofproto_parser
mac_address = ev.mac_address
network_id = ev.network_id
tunnel_key = ev.tunnel_key
remote_dpids = self.nw.get_dpids(network_id)
remote_dpids.remove(dpid)
# LOCAL_OUT_TABLE: unicast
# live-migration: there can be two ports with same mac_address
ports = self.nw.get_ports(dpid, network_id, mac_address)
assert ev.port_no in [port.port_no for port in ports]
rule = cls_rule(tun_id=tunnel_key, dl_dst=mac_address)
actions = [ofproto_parser.OFPActionOutput(port.port_no)
for port in ports if self._link_is_up(dp, port.port_no)]
self.send_flow_mod(dp, rule, self.LOCAL_OUT_TABLE, ofproto.OFPFC_ADD,
self.LOCAL_OUT_PRI_MAC, actions)
# LOCAL_OUT_TABLE: broad cast
rule = cls_rule(tun_id=tunnel_key, dl_dst=mac.BROADCAST)
actions = []
for port in self.nw.get_ports(dpid):
if not self._port_is_active(network_id, dp, port):
continue
actions.append(ofproto_parser.OFPActionOutput(port.port_no))
first_instance = (len(actions) == 1)
assert actions
if first_instance:
command = ofproto.OFPFC_ADD
else:
command = ofproto.OFPFC_MODIFY_STRICT
self.send_flow_mod(dp, rule, self.LOCAL_OUT_TABLE, command,
self.LOCAL_OUT_PRI_BROADCAST, actions)
# LOCAL_OUT_TABLE: multicast TODO:XXX
# LOCAL_OUT_TABLE: catch-all drop
if first_instance:
rule = cls_rule(tun_id=tunnel_key)
self.send_flow_mod(dp, rule, self.LOCAL_OUT_TABLE,
ofproto.OFPFC_ADD, self.LOCAL_OUT_PRI_DROP, [])
# TUNNEL_OUT_TABLE: unicast
mac_to_ports = collections.defaultdict(set)
for remote_dpid in remote_dpids:
remote_dp = self.dpset.get(remote_dpid)
if remote_dp is None:
continue
try:
tunnel_port_no = self.tunnels.get_port(dpid, remote_dpid)
except ryu_exc.PortNotFound:
continue
if not self._link_is_up(dp, tunnel_port_no):
continue
for port in self.nw.get_ports(remote_dpid):
if not self._port_is_active(network_id, remote_dp, port):
continue
# TUNNEL_OUT_TABLE: unicast
# live-migration: there can be more than one tunnel-ports that
# have a given mac address
mac_to_ports[port.mac_address].add(tunnel_port_no)
if first_instance:
# SRC_TABLE: TUNNEL-port: resubmit to LOAL_OUT_TABLE
rule = cls_rule(in_port=tunnel_port_no, tun_id=tunnel_key)
resubmit_table = ofproto_parser.NXActionResubmitTable(
in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
actions = [resubmit_table]
self.send_flow_mod(dp, rule, self.SRC_TABLE,
ofproto.OFPFC_ADD, self.SRC_PRI_TUNNEL_PASS,
actions)
# TUNNEL_OUT_TABLE: unicast
for remote_mac_address, tunnel_ports in mac_to_ports.items():
rule = cls_rule(tun_id=tunnel_key, dl_dst=remote_mac_address)
outputs = [ofproto_parser.OFPActionOutput(tunnel_port_no)
for tunnel_port_no in tunnel_ports]
resubmit_table = ofproto_parser.NXActionResubmitTable(
in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
actions = outputs + [resubmit_table]
self.send_flow_mod(dp, rule, self.TUNNEL_OUT_TABLE,
ofproto.OFPFC_ADD, self.TUNNEL_OUT_PRI_MAC,
actions)
if first_instance:
# TUNNEL_OUT_TABLE: catch-all(resubmit to LOCAL_OUT_TABLE)
rule = cls_rule(tun_id=tunnel_key)
resubmit_table = ofproto_parser.NXActionResubmitTable(
in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
actions = [resubmit_table]
self.send_flow_mod(dp, rule, self.TUNNEL_OUT_TABLE,
ofproto.OFPFC_ADD,
self.TUNNEL_OUT_PRI_PASS, actions)
# TUNNEL_OUT_TABLE: broadcast
rule = cls_rule(tun_id=tunnel_key, dl_dst=mac.BROADCAST)
actions = [ofproto_parser.OFPActionOutput(tunnel_port_no)
for tunnel_port_no
in self._list_tunnel_port(dp, remote_dpids)]
resubmit_table = ofproto_parser.NXActionResubmitTable(
in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
actions.append(resubmit_table)
self.send_flow_mod(dp, rule, self.TUNNEL_OUT_TABLE,
ofproto.OFPFC_ADD,
self.TUNNEL_OUT_PRI_BROADCAST, actions)
# TUNNEL_OUT_TABLE: multicast TODO:XXX
# SRC_TABLE: VM-port unicast
dp.send_barrier()
rule = cls_rule(in_port=ev.port_no, dl_src=mac_address)
set_tunnel = ofproto_parser.NXActionSetTunnel(tunnel_key)
resubmit_table = ofproto_parser.NXActionResubmitTable(
in_port=ofproto.OFPP_IN_PORT, table=self.TUNNEL_OUT_TABLE)
actions = [set_tunnel, resubmit_table]
self.send_flow_mod(dp, rule, self.SRC_TABLE, ofproto.OFPFC_ADD,
self.SRC_PRI_MAC, actions)
# SRC_TABLE: VM-port catch-call drop
rule = cls_rule(in_port=ev.port_no)
self.send_flow_mod(dp, rule, self.SRC_TABLE, ofproto.OFPFC_ADD,
self.SRC_PRI_DROP, [])
# remote dp
for remote_dpid in remote_dpids:
remote_dp = self.dpset.get(remote_dpid)
if remote_dp is None:
continue
try:
tunnel_port_no = self.tunnels.get_port(remote_dpid, dpid)
except ryu_exc.PortNotFound:
continue
if not self._link_is_up(remote_dp, tunnel_port_no):
continue
remote_ofproto = remote_dp.ofproto
remote_ofproto_parser = remote_dp.ofproto_parser
# TUNNEL_OUT_TABLE: unicast
# live-migration: there can be another port that has
# same mac address
tunnel_ports = self._tunnel_port_with_mac(remote_dp, dpid,
network_id, ev.port_no,
mac_address)
tunnel_ports.append(tunnel_port_no)
rule = cls_rule(tun_id=ev.tunnel_key, dl_dst=mac_address)
outputs = [remote_ofproto_parser.OFPActionOutput(port_no)
for port_no in tunnel_ports]
resubmit_table = remote_ofproto_parser.NXActionResubmitTable(
in_port=remote_ofproto.OFPP_IN_PORT,
table=self.LOCAL_OUT_TABLE)
actions = outputs + [resubmit_table]
self.send_flow_mod(remote_dp, rule, self.TUNNEL_OUT_TABLE,
remote_ofproto.OFPFC_ADD,
self.TUNNEL_OUT_PRI_MAC, actions)
if not first_instance:
continue
# SRC_TABLE: TUNNEL-port
rule = cls_rule(in_port=tunnel_port_no, tun_id=ev.tunnel_key)
resubmit_table = remote_ofproto_parser.NXActionResubmitTable(
in_port=remote_ofproto.OFPP_IN_PORT,
table=self.LOCAL_OUT_TABLE)
actions = [resubmit_table]
self.send_flow_mod(remote_dp, rule, self.SRC_TABLE,
remote_ofproto.OFPFC_ADD,
self.SRC_PRI_TUNNEL_PASS, actions)
# TUNNEL_OUT_TABLE: broadcast
rule = cls_rule(tun_id=ev.tunnel_key, dl_dst=mac.BROADCAST)
tunnel_ports = self._list_tunnel_port(remote_dp, remote_dpids)
if tunnel_port_no not in tunnel_ports:
tunnel_ports.append(tunnel_port_no)
actions = [remote_ofproto_parser.OFPActionOutput(port_no)
for port_no in tunnel_ports]
if len(actions) == 1:
command = remote_dp.ofproto.OFPFC_ADD
else:
command = remote_dp.ofproto.OFPFC_MODIFY_STRICT
resubmit_table = remote_ofproto_parser.NXActionResubmitTable(
in_port=remote_ofproto.OFPP_IN_PORT,
table=self.LOCAL_OUT_TABLE)
actions.append(resubmit_table)
self.send_flow_mod(remote_dp, rule, self.TUNNEL_OUT_TABLE,
command, self.TUNNEL_OUT_PRI_BROADCAST, actions)
# TUNNEL_OUT_TABLE: multicast TODO:XXX
def _vm_port_del(self, ev):
dpid = ev.dpid
dp = self.dpset.get(dpid)
assert dp is not None
ofproto = dp.ofproto
ofproto_parser = dp.ofproto_parser
mac_address = ev.mac_address
network_id = ev.network_id
tunnel_key = ev.tunnel_key
local_ports = []
for port in self.nw.get_ports(dpid):
if port.port_no == ev.port_no:
continue
if not self._port_is_active(network_id, dp, port):
continue
local_ports.append(port.port_no)
last_instance = not local_ports
# SRC_TABLE: VM-port unicast and catch-call
rule = cls_rule(in_port=ev.port_no)
self.send_flow_mod(dp, rule, self.SRC_TABLE, ofproto.OFPFC_DELETE,
ofproto.OFP_DEFAULT_PRIORITY,
[]) # priority is ignored
if last_instance:
# SRC_TABLE: TUNNEL-port: all tunnel matching
rule = cls_rule(tun_id=tunnel_key)
self.send_flow_mod(dp, rule, self.SRC_TABLE,
ofproto.OFPFC_DELETE,
ofproto.OFP_DEFAULT_PRIORITY,
[]) # priority is ignored
# TUNNEL_OUT_TABLE: (tun_id & dl_dst) and tun_id
rule = cls_rule(tun_id=tunnel_key)
self.send_flow_mod(dp, rule, self.TUNNEL_OUT_TABLE,
ofproto.OFPFC_DELETE,
ofproto.OFP_DEFAULT_PRIORITY,
[]) # priority is ignored
# LOCAL_OUT: tun_id catch-all drop rule
rule = cls_rule(tun_id=tunnel_key)
self.send_flow_mod(dp, rule, self.LOCAL_OUT_TABLE,
ofproto.OFPFC_DELETE,
ofproto.OFP_DEFAULT_PRIORITY,
[]) # priority is ignored
else:
# LOCAL_OUT_TABLE: unicast
# live-migration: there can be two ports with same mac_address
ports = self.nw.get_ports(dpid, network_id, mac_address)
port_nos = [port.port_no for port in ports
if (port.port_no != ev.port_no and
self._link_is_up(dp, port.port_no))]
rule = cls_rule(tun_id=tunnel_key, dl_dst=mac_address)
if port_nos:
assert len(ports) == 1
actions = [ofproto_parser.OFPActionOutput(port_no)
for port_no in port_nos]
self.send_flow_mod(dp, rule, self.LOCAL_OUT_TABLE,
ofproto.OFPFC_MODIFY_STRICT,
self.LOCAL_OUT_PRI_MAC, actions)
else:
self.send_flow_del(dp, rule, self.LOCAL_OUT_TABLE,
ofproto.OFPFC_DELETE_STRICT,
self.LOCAL_OUT_PRI_MAC, ev.port_no)
# LOCAL_OUT_TABLE: broadcast
rule = cls_rule(tun_id=tunnel_key, dl_dst=mac.BROADCAST)
actions = [ofproto_parser.OFPActionOutput(port_no)
for port_no in local_ports]
self.send_flow_mod(dp, rule, self.LOCAL_OUT_TABLE,
ofproto.OFPFC_MODIFY_STRICT,
self.LOCAL_OUT_PRI_BROADCAST, actions)
# LOCAL_OUT_TABLE: multicast TODO:XXX
# remote dp
remote_dpids = self.nw.get_dpids(ev.network_id)
if dpid in remote_dpids:
remote_dpids.remove(dpid)
for remote_dpid in remote_dpids:
remote_dp = self.dpset.get(remote_dpid)
if remote_dp is None:
continue
try:
tunnel_port_no = self.tunnels.get_port(remote_dpid, dpid)
except ryu_exc.PortNotFound:
continue
if not self._link_is_up(remote_dp, tunnel_port_no):
continue
remote_ofproto = remote_dp.ofproto
remote_ofproto_parser = remote_dp.ofproto_parser
if last_instance:
# SRC_TABLE: TUNNEL-port
rule = cls_rule(in_port=tunnel_port_no, tun_id=tunnel_key)
self.send_flow_del(remote_dp, rule, self.SRC_TABLE,
remote_ofproto.OFPFC_DELETE_STRICT,
self.SRC_PRI_TUNNEL_PASS, None)
# SRC_TABLE: TUNNEL-port catch-call drop rule
rule = cls_rule(in_port=tunnel_port_no)
self.send_flow_del(remote_dp, rule, self.SRC_TABLE,
remote_ofproto.OFPFC_DELETE_STRICT,
self.SRC_PRI_TUNNEL_DROP, None)
# TUNNEL_OUT_TABLE: broadcast
# tunnel_ports.remove(tunnel_port_no)
rule = cls_rule(tun_id=tunnel_key, dl_dst=mac.BROADCAST)
tunnel_ports = self._list_tunnel_port(remote_dp,
remote_dpids)
assert tunnel_port_no not in tunnel_ports
actions = [remote_ofproto_parser.OFPActionOutput(port_no)
for port_no in tunnel_ports]
if not actions:
command = remote_dp.ofproto.OFPFC_DELETE_STRICT
else:
command = remote_dp.ofproto.OFPFC_MODIFY_STRICT
resubmit_table = \
remote_ofproto_parser.NXActionResubmitTable(
in_port=remote_ofproto.OFPP_IN_PORT,
table=self.LOCAL_OUT_TABLE)
actions.append(resubmit_table)
self.send_flow_mod(remote_dp, rule, self.TUNNEL_OUT_TABLE,
command, self.TUNNEL_OUT_PRI_BROADCAST,
actions)
# TUNNEL_OUT_TABLE: unicast
# live-migration: there can be more than one (dpid, port_no)
# with a given mac address
tunnel_ports = self._tunnel_port_with_mac(remote_dp, dpid,
network_id, ev.port_no,
mac_address)
rule = cls_rule(tun_id=tunnel_key, dl_dst=mac_address)
if tunnel_ports:
outputs = [remote_ofproto_parser.OFPActionOutput(port_no)
for port_no in tunnel_ports]
resubmit_table = remote_ofproto_parser.NXActionResubmitTable(
in_port=remote_ofproto.OFPP_IN_PORT,
table=self.LOCAL_OUT_TABLE)
actions = outputs + [resubmit_table]
self.send_flow_mod(remote_dp, rule, self.TUNNEL_OUT_TABLE,
remote_ofproto.OFPFC_ADD,
self.TUNNEL_OUT_PRI_MAC, actions)
else:
self.send_flow_del(remote_dp, rule, self.TUNNEL_OUT_TABLE,
remote_ofproto.OFPFC_DELETE_STRICT,
self.TUNNEL_OUT_PRI_MAC, tunnel_port_no)
# TODO:XXX multicast
def _get_vm_ports(self, dpid):
ports = collections.defaultdict(list)
for port in self.nw.get_ports(dpid):
if port.network_id in RESERVED_NETWORK_IDS:
continue
ports[port.network_id].append(port)
return ports
def _tunnel_port_add(self, ev):
dpid = ev.dpid
dp = self.dpset.get(dpid)
ofproto = dp.ofproto
ofproto_parser = dp.ofproto_parser
remote_dpid = ev.remote_dpid
local_ports = self._get_vm_ports(dpid)
remote_ports = self._get_vm_ports(remote_dpid)
# SRC_TABLE: TUNNEL-port catch-call drop rule
# ingress flow from this tunnel port: remote -> tunnel port
# drop if unknown tunnel_key
rule = cls_rule(in_port=ev.port_no)
self.send_flow_mod(dp, rule, self.SRC_TABLE, ofproto.OFPFC_ADD,
self.SRC_PRI_TUNNEL_DROP, [])
# SRC_TABLE: TUNNEL-port: pass if known tunnel_key
for network_id in local_ports:
try:
tunnel_key = self.tunnels.get_key(network_id)
except tunnels.TunnelKeyNotFound:
continue
if network_id not in remote_ports:
continue
rule = cls_rule(in_port=ev.port_no, tun_id=tunnel_key)
resubmit_table = ofproto_parser.NXActionResubmitTable(
in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
actions = [resubmit_table]
self.send_flow_mod(dp, rule, self.SRC_TABLE, ofproto.OFPFC_ADD,
self.SRC_PRI_TUNNEL_PASS, actions)
# egress flow into this tunnel port: vm port -> tunnel port -> remote
for network_id in local_ports:
try:
tunnel_key = self.tunnels.get_key(network_id)
except tunnels.TunnelKeyNotFound:
continue
ports = remote_ports.get(network_id)
if ports is None:
continue
# TUNNEL_OUT_TABLE: unicast
for port in ports:
if port.mac_address is None:
continue
rule = cls_rule(tun_id=tunnel_key, dl_dst=port.mac_address)
output = ofproto_parser.OFPActionOutput(ev.port_no)
resubmit_table = ofproto_parser.NXActionResubmitTable(
in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
actions = [output, resubmit_table]
self.send_flow_mod(dp, rule, self.TUNNEL_OUT_TABLE,
ofproto.OFPFC_ADD, self.TUNNEL_OUT_PRI_MAC,
actions)
# TUNNEL_OUT_TABLE: broadcast
remote_dpids = self.nw.get_dpids(network_id)
remote_dpids.remove(dpid)
rule = cls_rule(tun_id=tunnel_key, dl_dst=mac.BROADCAST)
tunnel_ports = self._list_tunnel_port(dp, remote_dpids)
if ev.port_no not in tunnel_ports:
tunnel_ports.append(ev.port_no)
actions = [ofproto_parser.OFPActionOutput(port_no)
for port_no in tunnel_ports]
resubmit_table = ofproto_parser.NXActionResubmitTable(
in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
actions.append(resubmit_table)
if len(tunnel_ports) == 1:
command = ofproto.OFPFC_ADD
else:
command = ofproto.OFPFC_MODIFY_STRICT
self.send_flow_mod(dp, rule, self.TUNNEL_OUT_TABLE,
command, self.TUNNEL_OUT_PRI_BROADCAST, actions)
# TUNNEL_OUT_TABLE: multicast TODO:XXX
def _tunnel_port_del(self, ev):
# almost nothing to do because all flow related to this tunnel port
# should be handled by self._vm_port_del() as tunnel port deletion
# follows vm port deletion.
# the tunnel port is deleted if and only if no instance of same
# tenants resides in both nodes of tunnel end points.
self.logger.debug('tunnel_port_del %s', ev)
dp = self.dpset.get(ev.dpid)
# SRC_TABLE: TUNNEL-port catch-all drop rule
rule = cls_rule(in_port=ev.port_no)
self.send_flow_mod(dp, rule, self.SRC_TABLE,
dp.ofproto.OFPFC_DELETE_STRICT,
self.SRC_PRI_TUNNEL_DROP, [])
@handler.set_ev_handler(PortSet.EventTunnelKeyDel)
def tunnel_key_del_handler(self, ev):
self.logger.debug('tunnel_key_del ev %s', ev)
@handler.set_ev_handler(PortSet.EventVMPort)
def vm_port_handler(self, ev):
self.logger.debug('vm_port ev %s', ev)
if ev.add_del:
self._vm_port_add(ev)
else:
self._vm_port_del(ev)
@handler.set_ev_handler(PortSet.EventTunnelPort)
def tunnel_port_handler(self, ev):
self.logger.debug('tunnel_port ev %s', ev)
if ev.add_del:
self._tunnel_port_add(ev)
else:
self._tunnel_port_del(ev)
@handler.set_ev_handler(ofp_event.EventOFPPacketIn)
def packet_in_handler(self, ev):
# for debug
msg = ev.msg
self.logger.debug('packet in ev %s msg %s', ev, ev.msg)
if msg.buffer_id != msg.datapath.ofproto.OFP_NO_BUFFER:
msg.datapath.send_packet_out(msg.buffer_id, msg.in_port, [])

View File

@ -56,7 +56,7 @@ class GUIServerController(ControllerBase):
path = "%s/html/" % PATH
self.static_app = DirectoryApp(path)
@route('topology', '/{filename:[^/]*}')
@route('topology', '/{filename:.*}')
def static_handler(self, req, **kwargs):
if kwargs['filename']:
req.path_info = kwargs['filename']

View File

@ -16,37 +16,22 @@
# client for ryu.app.ofctl.service
import numbers
from ryu.base import app_manager
from . import event
def get_datapath(app, dpid=None):
def get_datapath(app, dpid):
"""
Get datapath object by dpid.
:param app: Client RyuApp instance
:param dpid: Datapath ID (int type) or None to get all datapath objects
:param dpid: Datapath-id (in integer)
Returns a object of datapath, a list of datapath objects when no dpid
given or None when error.
Raises an exception if any of the given values is invalid.
Example::
# ...(snip)...
import ryu.app.ofctl.api as ofctl_api
class MyApp(app_manager.RyuApp):
def _my_handler(self, ev):
# Get all datapath objects
result = ofctl_api.get_datapath(self)
# Get the datapath object which has the given dpid
result = ofctl_api.get_datapath(self, dpid=1)
Returns None on error.
"""
assert isinstance(dpid, numbers.Integral)
return app.send_request(event.GetDatapathRequest(dpid=dpid))()
@ -70,19 +55,12 @@ def send_msg(app, msg, reply_cls=None, reply_multi=False):
Example::
# ...(snip)...
import ryu.app.ofctl.api as ofctl_api
import ryu.app.ofctl.api as api
class MyApp(app_manager.RyuApp):
def _my_handler(self, ev):
# ...(snip)...
msg = parser.OFPPortDescStatsRequest(datapath=datapath)
result = ofctl_api.send_msg(
self, msg,
reply_cls=parser.OFPPortDescStatsReply,
reply_multi=True)
msg = parser.OFPPortDescStatsRequest(datapath=datapath)
result = api.send_msg(self, msg,
reply_cls=parser.OFPPortDescStatsReply,
reply_multi=True)
"""
return app.send_request(event.SendMsgRequest(msg=msg,
reply_cls=reply_cls,

View File

@ -33,8 +33,8 @@ class _ReplyBase(event.EventReplyBase):
# get datapath
class GetDatapathRequest(_RequestBase):
def __init__(self, dpid=None):
assert dpid is None or isinstance(dpid, numbers.Integral)
def __init__(self, dpid):
assert isinstance(dpid, numbers.Integral)
super(GetDatapathRequest, self).__init__()
self.dpid = dpid

View File

@ -64,22 +64,6 @@ class OfctlService(app_manager.RyuApp):
self.unobserve_event(ev_cls)
self.logger.debug('ofctl: stop observing %s', ev_cls)
def _cancel(self, info, barrier_xid, exception):
xid = info.barriers.pop(barrier_xid)
req = info.xids.pop(xid)
msg = req.msg
datapath = msg.datapath
parser = datapath.ofproto_parser
is_barrier = isinstance(msg, parser.OFPBarrierRequest)
info.results.pop(xid)
if not is_barrier and req.reply_cls is not None:
self._unobserve_msg(req.reply_cls)
self.logger.error('failed to send message <%s>', req.msg)
self.reply_to_request(req, event.Reply(exception=exception))
@staticmethod
def _is_error(msg):
return (ofp_event.ofp_msg_to_ev_cls(type(msg)) ==
@ -95,11 +79,6 @@ class OfctlService(app_manager.RyuApp):
self.logger.debug('add dpid %s datapath %s new_info %s old_info %s',
id, datapath, new_info, old_info)
self._switches[id] = new_info
if old_info:
old_info.datapath.close()
for xid in list(old_info.barriers):
self._cancel(
old_info, xid, exception.InvalidDatapath(result=id))
@set_ev_cls(ofp_event.EventOFPStateChange, DEAD_DISPATCHER)
def _handle_dead(self, ev):
@ -115,25 +94,23 @@ class OfctlService(app_manager.RyuApp):
if info.datapath is datapath:
self.logger.debug('forget info %s', info)
self._switches.pop(id)
for xid in list(info.barriers):
self._cancel(info, xid, exception.InvalidDatapath(result=id))
@set_ev_cls(event.GetDatapathRequest, MAIN_DISPATCHER)
def _handle_get_datapath(self, req):
result = None
if req.dpid is None:
result = [v.datapath for v in self._switches.values()]
else:
if req.dpid in self._switches:
result = self._switches[req.dpid].datapath
self.reply_to_request(req, event.Reply(result=result))
id = req.dpid
assert isinstance(id, numbers.Integral)
try:
datapath = self._switches[id].datapath
except KeyError:
datapath = None
self.logger.debug('dpid %s -> datapath %s', id, datapath)
rep = event.Reply(result=datapath)
self.reply_to_request(req, rep)
@set_ev_cls(event.SendMsgRequest, MAIN_DISPATCHER)
def _handle_send_msg(self, req):
msg = req.msg
datapath = msg.datapath
parser = datapath.ofproto_parser
is_barrier = isinstance(msg, parser.OFPBarrierRequest)
try:
si = self._switches[datapath.id]
@ -144,40 +121,28 @@ class OfctlService(app_manager.RyuApp):
self.reply_to_request(req, rep)
return
def _store_xid(xid, barrier_xid):
assert xid not in si.results
assert xid not in si.xids
assert barrier_xid not in si.barriers
si.results[xid] = []
si.xids[xid] = req
si.barriers[barrier_xid] = xid
if req.reply_cls is not None:
self._observe_msg(req.reply_cls)
if is_barrier:
barrier = msg
datapath.set_xid(barrier)
_store_xid(barrier.xid, barrier.xid)
else:
if req.reply_cls is not None:
self._observe_msg(req.reply_cls)
datapath.set_xid(msg)
barrier = datapath.ofproto_parser.OFPBarrierRequest(datapath)
datapath.set_xid(barrier)
_store_xid(msg.xid, barrier.xid)
if not datapath.send_msg(msg):
return self._cancel(
si, barrier.xid,
exception.InvalidDatapath(result=datapath.id))
datapath.set_xid(msg)
xid = msg.xid
barrier = datapath.ofproto_parser.OFPBarrierRequest(datapath)
datapath.set_xid(barrier)
barrier_xid = barrier.xid
assert xid not in si.results
assert xid not in si.xids
assert barrier_xid not in si.barriers
si.results[xid] = []
si.xids[xid] = req
si.barriers[barrier_xid] = xid
if not datapath.send_msg(barrier):
return self._cancel(
si, barrier.xid,
exception.InvalidDatapath(result=datapath.id))
datapath.send_msg(msg)
datapath.send_msg(barrier)
@set_ev_cls(ofp_event.EventOFPBarrierReply, MAIN_DISPATCHER)
def _handle_barrier(self, ev):
msg = ev.msg
datapath = msg.datapath
parser = datapath.ofproto_parser
try:
si = self._switches[datapath.id]
except KeyError:
@ -190,12 +155,9 @@ class OfctlService(app_manager.RyuApp):
return
result = si.results.pop(xid)
req = si.xids.pop(xid)
is_barrier = isinstance(req.msg, parser.OFPBarrierRequest)
if req.reply_cls is not None and not is_barrier:
if req.reply_cls is not None:
self._unobserve_msg(req.reply_cls)
if is_barrier and req.reply_cls == parser.OFPBarrierReply:
rep = event.Reply(result=ev.msg)
elif any(self._is_error(r) for r in result):
if any(self._is_error(r) for r in result):
rep = event.Reply(exception=exception.OFError(result=result))
elif req.reply_multi:
rep = event.Reply(result=result)
@ -223,7 +185,7 @@ class OfctlService(app_manager.RyuApp):
self.logger.error('unknown error xid %s', msg.xid)
return
if ((not isinstance(ev, ofp_event.EventOFPErrorMsg)) and
(req.reply_cls is None or not isinstance(ev.msg, req.reply_cls))):
(req.reply_cls is None or not isinstance(ev.msg, req.reply_cls))):
self.logger.error('unexpected reply %s for xid %s', ev, msg.xid)
return
try:

File diff suppressed because it is too large Load Diff

446
ryu/app/quantum_adapter.py Normal file
View File

@ -0,0 +1,446 @@
# 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.
"""
Listen OpenFlow port status change notifications from switches. Consult
ovsdb to retrieve the corresponding port uuid. Notify relevant parties,
including quantum (via Ryu plug-in) and Ryu applications. (via Ryu Events)
"""
import traceback
try:
from neutronclient import client as q_client
from neutronclient.common import exceptions as q_exc
from neutronclient.common.exceptions import (NeutronClientException as
client_exc)
from neutronclient.v2_0 import client as q_clientv2
except ImportError:
from quantumclient import client as q_client
from quantumclient.common import exceptions as q_exc
from quantumclient.common.exceptions import (QuantumClientException as
client_exc)
from quantumclient.v2_0 import client as q_clientv2
from ryu.app import conf_switch_key as cs_key
from ryu.app import rest_nw_id
from ryu.base import app_manager
from ryu.controller import (conf_switch,
dpset,
handler,
network)
from ryu import exception as ryu_exc
from ryu.lib import dpid as dpid_lib
from ryu.lib import mac as mac_lib
from ryu.lib import quantum_ifaces
from ryu.lib.ovs import bridge
from ryu.lib.quantum_ifaces import QuantumIfaces
def _get_auth_token(CONF, logger):
httpclient = q_client.HTTPClient(
username=CONF.neutron_admin_username,
tenant_name=CONF.neutron_admin_tenant_name,
password=CONF.neutron_admin_password,
auth_url=CONF.neutron_admin_auth_url,
timeout=CONF.neutron_url_timeout,
auth_strategy=CONF.neutron_auth_strategy)
try:
httpclient.authenticate()
except (q_exc.Unauthorized, q_exc.Forbidden, q_exc.EndpointNotFound) as e:
logger.error("authentication failure: %s", e)
return None
# logger.debug("_get_auth_token: token=%s", httpclient.auth_token)
return httpclient.auth_token
def _get_quantum_client(CONF, token):
if token:
my_client = q_clientv2.Client(
endpoint_url=CONF.neutron_url,
token=token, timeout=CONF.neutron_url_timeout)
else:
my_client = q_clientv2.Client(
endpoint_url=CONF.neutron_url,
auth_strategy=None, timeout=CONF.neutron_url_timeout)
return my_client
class OVSPort(object):
PORT_ERROR = -1
PORT_UNKNOWN = 0
PORT_GATEWAY = 1
PORT_VETH_GATEWAY = 2
PORT_GUEST = 3
PORT_TUNNEL = 4
# extra-ids: 'attached-mac', 'iface-id', 'iface-status', 'vm-uuid'
def __init__(self, ofport, port_name):
super(OVSPort, self).__init__()
self.ofport = ofport
self.name = port_name
self.type = None
self.ext_ids = {}
self.options = {}
def update(self, port):
self.__dict__.update((key, port[key]) for key
in ['name', 'ofport', 'type']
if key in port)
if 'external_ids' in port:
self.ext_ids = dict(port['external_ids'])
if 'options' in port:
self.options = dict(port['options'])
def get_port_type(self):
if not isinstance(self.ofport, int):
return self.PORT_ERROR
if self.type == 'internal' and 'iface-id' in self.ext_ids:
return self.PORT_GATEWAY
if self.type == '' and 'iface-id' in self.ext_ids:
return self.PORT_VETH_GATEWAY
if (self.type == 'gre' and 'local_ip' in self.options and
'remote_ip' in self.options):
return self.PORT_TUNNEL
if self.type == '' and 'vm-uuid' in self.ext_ids:
return self.PORT_GUEST
return self.PORT_UNKNOWN
def __str__(self):
return "type=%s ofport=%s name=%s, ext_ids=%s options=%s" % (
self.type, self.ofport, self.name, self.ext_ids, self.options)
def __eq__(self, other):
return (other is not None and
self.ofport == other.ofport and
self.type == other.type and
self.ext_ids == other.ext_ids and
self.options == other.options)
class OVSSwitch(object):
def __init__(self, CONF, dpid, nw, ifaces, logger):
# TODO: clean up
self.CONF = CONF
self.dpid = dpid
self.network_api = nw
self.ifaces = ifaces
self.logger = logger
self._q_api = None # lazy initialization
self.ctrl_addr = self.CONF.neutron_controller_addr
if not self.ctrl_addr:
raise ValueError('option neutron_controler_addr must be speicfied')
self.ovsdb_addr = None
self.tunnel_ip = None
self.ovs_bridge = None
self.ports = {} # port_no -> OVSPort
super(OVSSwitch, self).__init__()
@property
def q_api(self):
if self._q_api is None:
token = None
if self.CONF.neutron_auth_strategy:
token = _get_auth_token(self.CONF, self.logger)
self._q_api = _get_quantum_client(self.CONF, token)
return self._q_api
def set_ovsdb_addr(self, dpid, ovsdb_addr):
# easy check if the address format valid
self.logger.debug('set_ovsdb_addr dpid %s ovsdb_addr %s',
dpid_lib.dpid_to_str(dpid), ovsdb_addr)
_proto, _host, _port = ovsdb_addr.split(':')
old_address = self.ovsdb_addr
if old_address == ovsdb_addr:
return
if ovsdb_addr is None:
# TODO: clean up this ovs switch
if self.ovs_bridge:
self.ovs_bridge.del_controller()
self.ovs_bridge = None
return
self.ovsdb_addr = ovsdb_addr
if self.ovs_bridge is None:
self.logger.debug('ovsdb: adding ports %s', self.ports)
ovs_bridge = bridge.OVSBridge(self.CONF, dpid, ovsdb_addr)
self.ovs_bridge = ovs_bridge
ovs_bridge.init()
# TODO: for multi-controller
# not overwrite controllers, but append this controller
ovs_bridge.set_controller([self.ctrl_addr])
for port in self.ports.values():
self.logger.debug('adding port %s', port)
self.update_port(port.ofport, port.name, True)
def _update_external_port(self, port, add=True):
if add:
self.network_api.update_port(rest_nw_id.NW_ID_EXTERNAL,
self.dpid, port.ofport)
else:
self.network_api.remove_port(rest_nw_id.NW_ID_EXTERNAL,
self.dpid, port.ofport)
def _update_vif_port(self, port, add=True):
# When ovs port is updated, the corresponding network id may or
# may not exist because the order between the notification of
# ovs port deletion via OVSDB protocol and the notification
# network id/port deletion via REST from quantum plugin
# isn't deterministic.
self.logger.debug("_update_vif_port: %s %s", port, add)
iface_id = port.ext_ids.get('iface-id')
if iface_id is None:
return
try:
network_id = self.ifaces.get_key(iface_id,
QuantumIfaces.KEY_NETWORK_ID)
except KeyError:
return
if not add:
try:
self.network_api.remove_port(network_id,
self.dpid, port.ofport)
except (network.NetworkNotFound, ryu_exc.PortNotFound) as e:
self.logger.debug('remove_port %s', traceback.format_exc())
ports = self.ifaces.get_key(iface_id, QuantumIfaces.KEY_PORTS)
other_ovs_ports = None
for p in ports:
dpid = p.get(QuantumIfaces.SUBKEY_DATAPATH_ID)
if dpid is None:
continue
if dpid != self.dpid:
continue
other_ovs_ports = self.ifaces.del_key(iface_id,
QuantumIfaces.KEY_PORTS,
p)
if other_ovs_ports:
# When live-migration, one of the two OVS ports is deleted.
return
port_data = {
'status': 'DOWN'
}
body = {'port': port_data}
# self.logger.debug("port-body = %s", body)
try:
self.q_api.update_port(port.ext_ids['iface-id'], body)
except (q_exc.ConnectionFailed, client_exc) as e:
self.logger.error("quantum update port failed: %s", e)
# TODO: When authentication failure occurred,
# it should get auth token again
return
# update {network, port, mac}
try:
self.network_api.update_network(network_id)
self.network_api.update_port(network_id, self.dpid, port.ofport)
mac = port.ext_ids.get('attached-mac')
if mac:
self.network_api.update_mac(network_id, self.dpid, port.ofport,
mac_lib.haddr_to_bin(mac))
except (network.NetworkNotFound, ryu_exc.PortNotFound) as e:
self.logger.debug('update network/port/mac %s',
traceback.format_exc())
def update_port(self, port_no, port_name, add):
self.logger.debug('update_port port_no %d %s %s', port_no, port_name,
add)
assert port_name is not None
old_port = self.ports.get(port_no)
if not add:
new_port = None
self.ports.pop(port_no, None)
else:
new_port = OVSPort(port_no, port_name)
if self.ovs_bridge:
port_cfg = self.ovs_bridge.get_quantum_ports(port_name)
if port_cfg:
if 'ofport' not in port_cfg or not port_cfg['ofport']:
port_cfg['ofport'] = port_no
elif port_cfg['ofport'] != port_no:
self.logger.warn('inconsistent port_no: %d port_cfg '
'%s', port_no, port_cfg)
return
if port_cfg['name'] != port_name:
self.logger.warn('inconsistent port_name: %s '
'port_cfg %s', port_name, port_cfg)
return
new_port.update(port_cfg)
self.ports[port_no] = new_port
iface_id = new_port.ext_ids.get('iface-id')
if iface_id:
p = {QuantumIfaces.SUBKEY_DATAPATH_ID: self.dpid,
QuantumIfaces.SUBKEY_OFPORT: port_no,
QuantumIfaces.SUBKEY_NAME: port_name}
self.ifaces.update_key(iface_id, QuantumIfaces.KEY_PORTS, p)
if old_port == new_port:
return
if not new_port:
port_type = old_port.get_port_type()
if port_type == OVSPort.PORT_ERROR:
return
elif port_type == OVSPort.PORT_UNKNOWN:
# self.logger.info("delete external port: %s", old_port)
self._update_external_port(old_port, add=False)
else:
# self.logger.info("delete port: %s", old_port)
if port_type != OVSPort.PORT_TUNNEL:
self._update_vif_port(old_port, add=False)
return
if new_port.ofport == -1:
return
if not old_port or old_port.ofport == -1:
port_type = new_port.get_port_type()
if port_type == OVSPort.PORT_ERROR:
return
elif port_type == OVSPort.PORT_UNKNOWN:
# self.logger.info("create external port: %s", new_port)
self._update_external_port(new_port)
else:
# self.logger.info("create port: %s", new_port)
if port_type != OVSPort.PORT_TUNNEL:
self._update_vif_port(new_port)
return
if new_port.get_port_type() in (OVSPort.PORT_GUEST,
OVSPort.PORT_GATEWAY,
OVSPort.PORT_VETH_GATEWAY):
# self.logger.info("update port: %s", new_port)
self._update_vif_port(new_port)
class QuantumAdapter(app_manager.RyuApp):
_CONTEXTS = {
'conf_switch': conf_switch.ConfSwitchSet,
'network': network.Network,
'quantum_ifaces': quantum_ifaces.QuantumIfaces,
}
def __init__(self, *_args, **kwargs):
super(QuantumAdapter, self).__init__()
self.cs = kwargs['conf_switch']
self.nw = kwargs['network']
self.ifaces = kwargs['quantum_ifaces']
self.dps = {}
for network_id in rest_nw_id.RESERVED_NETWORK_IDS:
if network_id == rest_nw_id.NW_ID_UNKNOWN:
continue
self.nw.update_network(network_id)
def _get_ovs_switch(self, dpid, create=True):
ovs_switch = self.dps.get(dpid)
if not ovs_switch:
if create:
ovs_switch = OVSSwitch(self.CONF, dpid, self.nw, self.ifaces,
self.logger)
self.dps[dpid] = ovs_switch
else:
self.logger.debug('ovs switch %s is already known', dpid)
return ovs_switch
def _port_handler(self, dpid, port_no, port_name, add):
ovs_switch = self._get_ovs_switch(dpid)
if ovs_switch:
ovs_switch.update_port(port_no, port_name, add)
else:
self.logger.warn('unknown ovs switch %s %s %s %s\n',
dpid, port_no, port_name, add)
@handler.set_ev_cls(dpset.EventDP)
def dp_handler(self, ev):
dpid = ev.dp.id
ovs_switch = self._get_ovs_switch(dpid)
if not ovs_switch:
return
if ev.enter:
for port in ev.ports:
ovs_switch.update_port(port.port_no, port.name, True)
else:
# When dp leaving, we don't delete ports because OF connection
# can be disconnected for some reason.
# TODO: configuration needed to tell that this dp is really
# removed.
self.dps.pop(dpid, None)
@handler.set_ev_cls(dpset.EventPortAdd)
def port_add_handler(self, ev):
port = ev.port
name = port.name.rstrip('\0')
self._port_handler(ev.dp.id, port.port_no, name, True)
@handler.set_ev_cls(dpset.EventPortDelete)
def port_del_handler(self, ev):
port = ev.port
name = port.name.rstrip('\0')
self._port_handler(ev.dp.id, port.port_no, name, False)
def _conf_switch_set_ovsdb_addr(self, dpid, value):
ovs_switch = self._get_ovs_switch(dpid)
ovs_switch.set_ovsdb_addr(dpid, value)
def _conf_switch_del_ovsdb_addr(self, dpid):
ovs_switch = self._get_ovs_switch(dpid, False)
if ovs_switch:
ovs_switch.set_ovsdb_addr(dpid, None)
@handler.set_ev_cls(conf_switch.EventConfSwitchSet)
def conf_switch_set_handler(self, ev):
self.logger.debug("conf_switch set: %s", ev)
if ev.key == cs_key.OVSDB_ADDR:
self._conf_switch_set_ovsdb_addr(ev.dpid, ev.value)
else:
self.logger.debug("unknown event: %s", ev)
@handler.set_ev_cls(conf_switch.EventConfSwitchDel)
def conf_switch_del_handler(self, ev):
self.logger.debug("conf_switch del: %s", ev)
if ev.key == cs_key.OVSDB_ADDR:
self._conf_switch_del_ovsdb_addr(ev.dpid)
else:
self.logger.debug("unknown event: %s", ev)
@handler.set_ev_cls(quantum_ifaces.EventQuantumIfaceSet)
def quantum_iface_set_handler(self, ev):
if ev.key != quantum_ifaces.QuantumIfaces.KEY_NETWORK_ID:
# self.logger.debug("unknown key %s", ev.key)
return
iface_id = ev.iface_id
try:
ports = self.ifaces.get_key(iface_id, QuantumIfaces.KEY_PORTS)
except KeyError:
return
for p in ports:
try:
dpid = p[QuantumIfaces.SUBKEY_DATAPATH_ID]
ofport = p[QuantumIfaces.SUBKEY_OFPORT]
port_name = p[QuantumIfaces.SUBKEY_NAME]
except KeyError:
continue
ovs_switch = self._get_ovs_switch(dpid, False)
if ovs_switch:
ovs_switch.update_port(ofport, port_name, True)

270
ryu/app/rest.py Normal file
View File

@ -0,0 +1,270 @@
# 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 basic set of REST API.
- Network registration
- End-point port management
- OpenFlow port number
- MAC address (for anti-spoofing)
Used by OpenStack Ryu plug-in.
"""
import json
from webob import Response
from ryu.app import wsgi as app_wsgi
from ryu.app.wsgi import ControllerBase, WSGIApplication
from ryu.base import app_manager
from ryu.controller import network
from ryu.exception import NetworkNotFound, NetworkAlreadyExist
from ryu.exception import PortNotFound, PortAlreadyExist
from ryu.lib import dpid as dpid_lib
from ryu.lib import mac as mac_lib
# TODO:XXX
# define db interface and store those information into db
# REST API
# get the list of networks
# GET /v1.0/networks/
#
# register a new network.
# Fail if the network is already registered.
# POST /v1.0/networks/{network-id}
#
# update a new network.
# Success as nop even if the network is already registered.
#
# PUT /v1.0/networks/{network-id}
#
# remove a network
# DELETE /v1.0/networks/{network-id}
#
# get the list of sets of dpid and port
# GET /v1.0/networks/{network-id}/
#
# register a new set of dpid and port
# Fail if the port is already registered.
# POST /v1.0/networks/{network-id}/{dpid}_{port-id}
#
# update a new set of dpid and port
# Success as nop even if same port already registered
# PUT /v1.0/networks/{network-id}/{dpid}_{port-id}
#
# remove a set of dpid and port
# DELETE /v1.0/networks/{network-id}/{dpid}_{port-id}
#
# get the list of mac addresses of dpid and port
# GET /v1.0/networks/{network-id}/{dpid}_{port-id}/macs/
#
# register a new mac address for dpid and port
# Fail if mac address is already registered or the mac address is used
# for other ports of the same network-id
# POST /v1.0/networks/{network-id}/{dpid}_{port-id}/macs/{mac}
#
# update a new mac address for dpid and port
# Success as nop even if same mac address is already registered.
# For now, changing mac address is not allows as it fails.
# PUT /v1.0/networks/{network-id}/{dpid}_{port-id}/macs/{mac}
#
# For now DELETE /v1.0/networks/{network-id}/{dpid}_{port-id}/macs/{mac}
# is not supported. mac address is released when port is deleted.
#
class NetworkController(ControllerBase):
def __init__(self, req, link, data, **config):
super(NetworkController, self).__init__(req, link, data, **config)
self.nw = data
def create(self, req, network_id, **_kwargs):
try:
self.nw.create_network(network_id)
except NetworkAlreadyExist:
return Response(status=409)
else:
return Response(status=200)
def update(self, req, network_id, **_kwargs):
self.nw.update_network(network_id)
return Response(status=200)
def lists(self, req, **_kwargs):
body = json.dumps(self.nw.list_networks())
return Response(content_type='application/json', body=body)
def delete(self, req, network_id, **_kwargs):
try:
self.nw.remove_network(network_id)
except NetworkNotFound:
return Response(status=404)
return Response(status=200)
class PortController(ControllerBase):
def __init__(self, req, link, data, **config):
super(PortController, self).__init__(req, link, data, **config)
self.nw = data
def create(self, req, network_id, dpid, port_id, **_kwargs):
dpid = dpid_lib.str_to_dpid(dpid)
port_id = int(port_id)
try:
self.nw.create_port(network_id, dpid, port_id)
except NetworkNotFound:
return Response(status=404)
except PortAlreadyExist:
return Response(status=409)
return Response(status=200)
def update(self, req, network_id, dpid, port_id, **_kwargs):
dpid = dpid_lib.str_to_dpid(dpid)
port_id = int(port_id)
try:
self.nw.update_port(network_id, dpid, port_id)
except NetworkNotFound:
return Response(status=404)
return Response(status=200)
def lists(self, req, network_id, **_kwargs):
try:
body = json.dumps(self.nw.list_ports(network_id))
except NetworkNotFound:
return Response(status=404)
return Response(content_type='application/json', body=body)
def delete(self, req, network_id, dpid, port_id, **_kwargs):
dpid = dpid_lib.str_to_dpid(dpid)
port_id = int(port_id)
try:
self.nw.remove_port(network_id, dpid, port_id)
except (NetworkNotFound, PortNotFound):
return Response(status=404)
return Response(status=200)
class MacController(ControllerBase):
def __init__(self, req, link, data, **config):
super(MacController, self).__init__(req, link, data, **config)
self.nw = data
def create(self, _req, network_id, dpid, port_id, mac_addr, **_kwargs):
dpid = dpid_lib.str_to_dpid(dpid)
port_id = int(port_id)
mac_addr = mac_lib.haddr_to_bin(mac_addr)
try:
self.nw.create_mac(network_id, dpid, port_id, mac_addr)
except PortNotFound:
return Response(status=404)
except network.MacAddressAlreadyExist:
return Response(status=409)
return Response(status=200)
def update(self, _req, network_id, dpid, port_id, mac_addr, **_kwargs):
dpid = dpid_lib.str_to_dpid(dpid)
port_id = int(port_id)
mac_addr = mac_lib.haddr_to_bin(mac_addr)
try:
self.nw.update_mac(network_id, dpid, port_id, mac_addr)
except PortNotFound:
return Response(status=404)
return Response(status=200)
def lists(self, _req, network_id, dpid, port_id, **_kwargs):
dpid = dpid_lib.str_to_dpid(dpid)
port_id = int(port_id)
try:
body = json.dumps([mac_lib.haddr_to_str(mac_addr) for mac_addr in
self.nw.list_mac(dpid, port_id)])
except PortNotFound:
return Response(status=404)
return Response(content_type='application/json', body=body)
class RestAPI(app_manager.RyuApp):
_CONTEXTS = {
'network': network.Network,
'wsgi': WSGIApplication
}
def __init__(self, *args, **kwargs):
super(RestAPI, self).__init__(*args, **kwargs)
self.nw = kwargs['network']
wsgi = kwargs['wsgi']
mapper = wsgi.mapper
wsgi.registory['NetworkController'] = self.nw
route_name = 'networks'
uri = '/v1.0/networks'
mapper.connect(route_name, uri,
controller=NetworkController, action='lists',
conditions=dict(method=['GET', 'HEAD']))
uri += '/{network_id}'
s = mapper.submapper(controller=NetworkController)
s.connect(route_name, uri, action='create',
conditions=dict(method=['POST']))
s.connect(route_name, uri, action='update',
conditions=dict(method=['PUT']))
s.connect(route_name, uri, action='delete',
conditions=dict(method=['DELETE']))
wsgi.registory['PortController'] = self.nw
route_name = 'ports'
mapper.connect(route_name, uri,
controller=PortController, action='lists',
conditions=dict(method=['GET']))
uri += '/{dpid}_{port_id}'
requirements = {'dpid': dpid_lib.DPID_PATTERN,
'port_id': app_wsgi.DIGIT_PATTERN}
s = mapper.submapper(controller=PortController,
requirements=requirements)
s.connect(route_name, uri, action='create',
conditions=dict(method=['POST']))
s.connect(route_name, uri, action='update',
conditions=dict(method=['PUT']))
s.connect(route_name, uri, action='delete',
conditions=dict(method=['DELETE']))
wsgi.registory['MacController'] = self.nw
route_name = 'macs'
uri += '/macs'
mapper.connect(route_name, uri,
controller=MacController, action='lists',
conditions=dict(method=['GET']),
requirements=requirements)
uri += '/{mac_addr}'
requirements['mac_addr'] = mac_lib.HADDR_PATTERN
s = mapper.submapper(controller=MacController,
requirements=requirements)
s.connect(route_name, uri, action='create',
conditions=dict(method=['POST']))
s.connect(route_name, uri, action='update',
conditions=dict(method=['PUT']))

View File

@ -21,12 +21,12 @@ This module provides a set of REST API for switch configuration.
Used by OpenStack Ryu agent.
"""
import json
from six.moves import http_client
import json
import logging
from webob import Response
from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.base import app_manager
from ryu.controller import conf_switch
from ryu.lib import dpid as dpid_lib
@ -111,11 +111,7 @@ class ConfSwitchController(ControllerBase):
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)
val = json.loads(req.body)
self.conf_switch.set_key(dpid, key, val)
return None

View File

@ -17,8 +17,9 @@
import logging
import json
from webob import Response
from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.app.wsgi import WSGIApplication
from ryu.base import app_manager
from ryu.controller import ofp_event
@ -491,8 +492,8 @@ class FirewallController(ControllerBase):
def _set_rule(self, req, switchid, vlan_id=VLANID_NONE):
try:
rule = req.json if req.body else {}
except ValueError:
rule = json.loads(req.body)
except SyntaxError:
FirewallController._LOGGER.debug('invalid syntax %s', req.body)
return Response(status=400)
@ -515,8 +516,8 @@ class FirewallController(ControllerBase):
def _delete_rule(self, req, switchid, vlan_id=VLANID_NONE):
try:
ruleid = req.json if req.body else {}
except ValueError:
ruleid = json.loads(req.body)
except SyntaxError:
FirewallController._LOGGER.debug('invalid syntax %s', req.body)
return Response(status=400)
@ -678,7 +679,8 @@ class Firewall(object):
def _set_log_status(self, is_enable, waiters):
if is_enable:
actions = Action.to_openflow({REST_ACTION: REST_ACTION_PACKETIN})
actions = Action.to_openflow(self.dp,
{REST_ACTION: REST_ACTION_PACKETIN})
details = 'Log collection started.'
else:
actions = []
@ -720,7 +722,7 @@ class Firewall(object):
priority = ARP_FLOW_PRIORITY
match = {REST_DL_TYPE: ether.ETH_TYPE_ARP}
action = {REST_ACTION: REST_ACTION_ALLOW}
actions = Action.to_openflow(action)
actions = Action.to_openflow(self.dp, action)
flow = self._to_of_flow(cookie=cookie, priority=priority,
match=match, actions=actions)
@ -752,7 +754,7 @@ class Firewall(object):
result = self.get_log_status(waiters)
if result[REST_LOG_STATUS] == REST_STATUS_ENABLE:
rest[REST_ACTION] = REST_ACTION_PACKETIN
actions = Action.to_openflow(rest)
actions = Action.to_openflow(self.dp, rest)
flow = self._to_of_flow(cookie=cookie, priority=priority,
match=match, actions=actions)
@ -879,7 +881,7 @@ class Firewall(object):
rule = {REST_RULE_ID: ruleid}
rule.update({REST_PRIORITY: flow[REST_PRIORITY]})
rule.update(Match.to_rest(flow))
rule.update(Action.to_rest(flow))
rule.update(Action.to_rest(self.dp, flow))
return rule
@ -1077,17 +1079,19 @@ class Match(object):
class Action(object):
@staticmethod
def to_openflow(rest):
def to_openflow(dp, rest):
value = rest.get(REST_ACTION, REST_ACTION_ALLOW)
if value == REST_ACTION_ALLOW:
out_port = dp.ofproto.OFPP_NORMAL
action = [{'type': 'OUTPUT',
'port': 'NORMAL'}]
'port': out_port}]
elif value == REST_ACTION_DENY:
action = []
elif value == REST_ACTION_PACKETIN:
out_port = dp.ofproto.OFPP_CONTROLLER
action = [{'type': 'OUTPUT',
'port': 'CONTROLLER',
'port': out_port,
'max_len': 128}]
else:
raise ValueError('Invalid action type.')
@ -1095,9 +1099,9 @@ class Action(object):
return action
@staticmethod
def to_rest(openflow):
def to_rest(dp, openflow):
if REST_ACTION in openflow:
action_allow = 'OUTPUT:NORMAL'
action_allow = 'OUTPUT:%d' % dp.ofproto.OFPP_NORMAL
if openflow[REST_ACTION] == [action_allow]:
action = {REST_ACTION: REST_ACTION_ALLOW}
else:

41
ryu/app/rest_nw_id.py Normal file
View File

@ -0,0 +1,41 @@
# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2011, 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.
NW_ID_EXTERNAL = '__NW_ID_EXTERNAL__'
NW_ID_RESERVED = '__NW_ID_RESERVED__'
NW_ID_VPORT_GRE = '__NW_ID_VPORT_GRE__'
NW_ID_UNKNOWN = '__NW_ID_UNKNOWN__'
RESERVED_NETWORK_IDS = (
NW_ID_EXTERNAL,
NW_ID_RESERVED,
NW_ID_VPORT_GRE,
NW_ID_UNKNOWN,
)
# tunnel type
_TUNNEL_TYPE_TO_NETWORK_ID = {
'gre': NW_ID_VPORT_GRE,
}
def tunnel_type_to_network_id(tunnel_type):
return _TUNNEL_TYPE_TO_NETWORK_ID[tunnel_type.lower()]
# PORT_TYPE_VM = 'guestvm'
# PORT_TYPE_GW = 'gateway'
# PORT_TYPE_EXTERNAL = 'external'

View File

@ -18,11 +18,10 @@ import logging
import json
import re
from webob import Response
from ryu.app import conf_switch_key as cs_key
from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.app.wsgi import route
from ryu.app.wsgi import WSGIApplication
from ryu.app.wsgi import ControllerBase, WSGIApplication, route
from ryu.base import app_manager
from ryu.controller import conf_switch
from ryu.controller import ofp_event
@ -425,8 +424,7 @@ class QoSController(ControllerBase):
@staticmethod
def delete_ovsdb_addr(dpid):
ofs = QoSController._OFS_LIST.get(dpid, None)
if ofs is not None:
ofs.set_ovsdb_addr(dpid, None)
ofs.set_ovsdb_addr(dpid, None)
@route('qos_switch', BASE_URL + '/queue/{switchid}',
methods=['GET'], requirements=REQUIREMENTS)
@ -508,8 +506,8 @@ class QoSController(ControllerBase):
def _access_switch(self, req, switchid, vlan_id, func, waiters):
try:
rest = req.json if req.body else {}
except ValueError:
rest = json.loads(req.body) if req.body else {}
except SyntaxError:
QoSController._LOGGER.debug('invalid syntax %s', req.body)
return Response(status=400)
@ -558,22 +556,6 @@ class QoS(object):
self.vlan_list[VLANID_NONE] = 0 # for VLAN=None
self.dp = dp
self.version = dp.ofproto.OFP_VERSION
# Dictionary of port name to Queue config.
# e.g.)
# self.queue_list = {
# "s1-eth1": {
# "0": {
# "config": {
# "max-rate": "600000"
# }
# },
# "1": {
# "config": {
# "min-rate": "900000"
# }
# }
# }
# }
self.queue_list = {}
self.CONF = CONF
self.ovsdb_addr = None
@ -601,22 +583,25 @@ class QoS(object):
self.ofctl.mod_flow_entry(self.dp, flow, cmd)
def set_ovsdb_addr(self, dpid, ovsdb_addr):
# easy check if the address format valid
_proto, _host, _port = ovsdb_addr.split(':')
old_address = self.ovsdb_addr
if old_address == ovsdb_addr:
return
elif ovsdb_addr is None:
# Determine deleting OVSDB address was requested.
if ovsdb_addr is None:
if self.ovs_bridge:
self.ovs_bridge.del_controller()
self.ovs_bridge = None
return
ovs_bridge = bridge.OVSBridge(self.CONF, dpid, ovsdb_addr)
try:
ovs_bridge.init()
except:
raise ValueError('ovsdb addr is not available.')
self.ovsdb_addr = ovsdb_addr
self.ovs_bridge = ovs_bridge
if self.ovs_bridge is None:
ovs_bridge = bridge.OVSBridge(self.CONF, dpid, ovsdb_addr)
self.ovs_bridge = ovs_bridge
try:
ovs_bridge.init()
except:
raise ValueError('ovsdb addr is not available.')
def _update_vlan_list(self, vlan_list):
for vlan_id in self.vlan_list.keys():
@ -679,15 +664,7 @@ class QoS(object):
'details': 'ovs_bridge is not exists'}
return REST_COMMAND_RESULT, msg
port_name = rest.get(REST_PORT_NAME, None)
vif_ports = self.ovs_bridge.get_port_name_list()
if port_name is not None:
if port_name not in vif_ports:
raise ValueError('%s port is not exists' % port_name)
vif_ports = [port_name]
queue_list = {}
self.queue_list.clear()
queue_type = rest.get(REST_QUEUE_TYPE, 'linux-htb')
parent_max_rate = rest.get(REST_QUEUE_MAX_RATE, None)
queues = rest.get(REST_QUEUES, [])
@ -705,9 +682,17 @@ class QoS(object):
config['min-rate'] = min_rate
if len(config):
queue_config.append(config)
queue_list[queue_id] = {'config': config}
self.queue_list[queue_id] = {'config': config}
queue_id += 1
port_name = rest.get(REST_PORT_NAME, None)
vif_ports = self.ovs_bridge.get_port_name_list()
if port_name is not None:
if port_name not in vif_ports:
raise ValueError('%s port is not exists' % port_name)
vif_ports = [port_name]
for port_name in vif_ports:
try:
self.ovs_bridge.set_qos(port_name, type=queue_type,
@ -715,10 +700,9 @@ class QoS(object):
queues=queue_config)
except Exception as msg:
raise ValueError(msg)
self.queue_list[port_name] = queue_list
msg = {'result': 'success',
'details': queue_list}
'details': self.queue_list}
return REST_COMMAND_RESULT, msg
@ -733,9 +717,9 @@ class QoS(object):
@rest_command
def delete_queue(self, rest, vlan_id):
self.queue_list.clear()
if self._delete_queue():
msg = 'success'
self.queue_list.clear()
else:
msg = 'failure'
@ -1143,20 +1127,20 @@ class Match(object):
class Action(object):
@staticmethod
def to_rest(flow):
if REST_ACTION in flow:
def to_rest(openflow):
if REST_ACTION in openflow:
actions = []
for act in flow[REST_ACTION]:
field_value = re.search(r'SET_FIELD: \{ip_dscp:(\d+)', act)
for action in openflow[REST_ACTION]:
field_value = re.search('SET_FIELD: {ip_dscp:(\d+)', action)
if field_value:
actions.append({REST_ACTION_MARK: field_value.group(1)})
meter_value = re.search(r'METER:(\d+)', act)
meter_value = re.search('METER:(\d+)', action)
if meter_value:
actions.append({REST_ACTION_METER: meter_value.group(1)})
queue_value = re.search(r'SET_QUEUE:(\d+)', act)
queue_value = re.search('SET_QUEUE:(\d+)', action)
if queue_value:
actions.append({REST_ACTION_QUEUE: queue_value.group(1)})
action = {REST_ACTION: actions}
action = {REST_ACTION: actions}
else:
action = {REST_ACTION: 'Unknown action type.'}

136
ryu/app/rest_quantum.py Normal file
View File

@ -0,0 +1,136 @@
# 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 dedicated to OpenStack Ryu plug-in.
- Interface (uuid in ovsdb) registration
- Maintain interface association to a network
Used by OpenStack Ryu plug-in.
"""
import json
from webob import Response
from ryu.base import app_manager
from ryu.app.wsgi import (ControllerBase,
WSGIApplication)
from ryu.lib import quantum_ifaces
# REST API for openstack quantum
# get the list of iface-ids
# GET /v1.0/quantum/ports/
#
# register the iface_id
# POST /v1.0/quantum/ports/{iface_id}
#
# unregister iface_id
# DELETE /v1.0/quantum/ports/{iface_id}
#
# associate network_id with iface_id
# GET /v1.0/quantum/ports/{iface_id}/network_id
#
# associate network_id with iface_id
# POST /v1.0/quantum/ports/{iface_id}/network_id/{network_id}
#
# update network_id with iface_id
# PUT /v1.0/quantum/ports/{iface_id}/network_id/{network_id}
class QuantumController(ControllerBase):
def __init__(self, req, link, data, **config):
super(QuantumController, self).__init__(req, link, data, **config)
self.ifaces = data
def list_ifaces(self, _req, **_kwargs):
body = json.dumps(list(self.ifaces.keys()))
return Response(content_type='application/json', body=body)
def delete_iface(self, _req, iface_id, **_kwargs):
self.ifaces.unregister(iface_id)
return Response(status=200)
def list_keys(self, _req, iface_id, **_kwargs):
try:
keys = self.ifaces.list_keys(iface_id)
except KeyError:
return Response(status=404)
body = json.dumps(keys)
return Response(content_type='application/json', body=body)
def get_key(self, _req, iface_id, key, **_kwargs):
try:
value = self.ifaces.get_key(iface_id, key)
except KeyError:
return Response(status=404)
body = json.dumps(value)
return Response(content_type='application/json', body=body)
def create_value(self, _req, iface_id, key, value, **_kwargs):
try:
self.ifaces.set_key(iface_id, key, value)
except ValueError:
return Response(status=404)
return Response(status=200)
def update_value(self, _req, iface_id, key, value, **_kwargs):
try:
self.ifaces.update_key(iface_id, key, value)
except ValueError:
return Response(status=404)
return Response(status=200)
class QuantumIfaceAPI(app_manager.RyuApp):
_CONTEXTS = {
'quantum_ifaces': quantum_ifaces.QuantumIfaces,
'wsgi': WSGIApplication,
}
def __init__(self, *args, **kwargs):
super(QuantumIfaceAPI, self).__init__(*args, **kwargs)
self.ifaces = kwargs['quantum_ifaces']
wsgi = kwargs['wsgi']
mapper = wsgi.mapper
controller = QuantumController
wsgi.registory[controller.__name__] = self.ifaces
route_name = 'quantum_ifaces'
uri = '/v1.0/quantum'
ports_uri = uri + '/ports'
s = mapper.submapper(controller=controller)
s.connect(route_name, ports_uri, action='list_ifaces',
conditions=dict(method=['GET', 'HEAD']))
iface_uri = ports_uri + '/{iface_id}'
s.connect(route_name, iface_uri, action='delete_iface',
conditions=dict(method=['DELETE']))
keys_uri = iface_uri + '/keys'
s.connect(route_name, keys_uri, action='list_keys',
conditions=dict(method=['GET', 'HEAD']))
key_uri = keys_uri + '/{key}'
s.connect(route_name, key_uri, action='get_key',
conditions=dict(method=['GET', 'HEAD']))
value_uri = keys_uri + '/{key}/{value}'
s.connect(route_name, value_uri, action='create_value',
conditions=dict(method=['POST']))
s.connect(route_name, value_uri, action='update_value',
conditions=dict(method=['PUT']))

View File

@ -20,9 +20,9 @@ import socket
import struct
import json
from webob import Response
from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.app.wsgi import WSGIApplication
from ryu.base import app_manager
from ryu.controller import dpset
@ -40,7 +40,6 @@ from ryu.lib.packet import ethernet
from ryu.lib.packet import icmp
from ryu.lib.packet import ipv4
from ryu.lib.packet import packet
from ryu.lib.packet import packet_base
from ryu.lib.packet import tcp
from ryu.lib.packet import udp
from ryu.lib.packet import vlan
@ -377,45 +376,42 @@ class RouterController(ControllerBase):
@rest_command
def get_data(self, req, switch_id, **_kwargs):
return self._access_router(switch_id, VLANID_NONE,
'get_data', req)
'get_data', req.body)
# GET /router/{switch_id}/{vlan_id}
@rest_command
def get_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
return self._access_router(switch_id, vlan_id,
'get_data', req)
'get_data', req.body)
# POST /router/{switch_id}
@rest_command
def set_data(self, req, switch_id, **_kwargs):
return self._access_router(switch_id, VLANID_NONE,
'set_data', req)
'set_data', req.body)
# POST /router/{switch_id}/{vlan_id}
@rest_command
def set_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
return self._access_router(switch_id, vlan_id,
'set_data', req)
'set_data', req.body)
# DELETE /router/{switch_id}
@rest_command
def delete_data(self, req, switch_id, **_kwargs):
return self._access_router(switch_id, VLANID_NONE,
'delete_data', req)
'delete_data', req.body)
# DELETE /router/{switch_id}/{vlan_id}
@rest_command
def delete_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
return self._access_router(switch_id, vlan_id,
'delete_data', req)
'delete_data', req.body)
def _access_router(self, switch_id, vlan_id, func, req):
def _access_router(self, switch_id, vlan_id, func, rest_param):
rest_message = []
routers = self._get_router(switch_id)
try:
param = req.json if req.body else {}
except ValueError:
raise SyntaxError('invalid syntax %s', req.body)
param = json.loads(rest_param) if rest_param else {}
for router in routers.values():
function = getattr(router, func)
data = function(vlan_id, param, self.waiters)
@ -570,8 +566,7 @@ class Router(dict):
# TODO: Packet library convert to string
# self.logger.debug('Packet in = %s', str(pkt), self.sw_id)
header_list = dict((p.protocol_name, p)
for p in pkt.protocols
if isinstance(p, packet_base.PacketBase))
for p in pkt.protocols if type(p) != str)
if header_list:
# Check vlan-tag
vlan_id = VLANID_NONE
@ -1010,14 +1005,14 @@ class VlanRouter(object):
else:
if header_list[ARP].opcode == arp.ARP_REQUEST:
# ARP request to router port -> send ARP reply
src_mac = self.port_data[in_port].mac
dst_mac = header_list[ARP].src_mac
src_mac = header_list[ARP].src_mac
dst_mac = self.port_data[in_port].mac
arp_target_mac = dst_mac
output = in_port
in_port = self.ofctl.dp.ofproto.OFPP_CONTROLLER
self.ofctl.send_arp(arp.ARP_REPLY, self.vlan_id,
src_mac, dst_mac, dst_ip, src_ip,
dst_mac, src_mac, dst_ip, src_ip,
arp_target_mac, in_port, output)
log_msg = 'Receive ARP request from [%s] to router port [%s].'
@ -1528,37 +1523,18 @@ class OfCtl(object):
eth = protocol_list[ETHERNET]
e = ethernet.ethernet(eth.src, eth.dst, ether_proto)
ip = protocol_list[IPV4]
if icmp_data is None and msg_data is not None:
# RFC 4884 says that we should send "at least 128 octets"
# if we are using the ICMP Extension Structure.
# We're not using the extension structure, but let's send
# up to 128 bytes of the original msg_data.
#
# RFC 4884 also states that the length field is interpreted in
# 32 bit units, so the length calculated in bytes needs to first
# be divided by 4, then increased by 1 if the modulus is non-zero.
#
# Finally, RFC 4884 says, if we're specifying the length, we MUST
# zero pad to the next 32 bit boundary.
end_of_data = offset + len(ip) + 128
ip_datagram = bytearray()
ip_datagram += msg_data[offset:end_of_data]
data_len = int(len(ip_datagram) / 4)
length_modulus = int(len(ip_datagram) % 4)
if length_modulus:
data_len += 1
ip_datagram += bytearray([0] * (4 - length_modulus))
ip_datagram = msg_data[offset:]
if icmp_type == icmp.ICMP_DEST_UNREACH:
icmp_data = icmp.dest_unreach(data_len=data_len,
icmp_data = icmp.dest_unreach(data_len=len(ip_datagram),
data=ip_datagram)
elif icmp_type == icmp.ICMP_TIME_EXCEEDED:
icmp_data = icmp.TimeExceeded(data_len=data_len,
icmp_data = icmp.TimeExceeded(data_len=len(ip_datagram),
data=ip_datagram)
ic = icmp.icmp(icmp_type, icmp_code, csum, data=icmp_data)
ip = protocol_list[IPV4]
if src_ip is None:
src_ip = ip.dst
ip_total_length = ip.header_length * 4 + ic._MIN_LEN

View File

@ -14,11 +14,9 @@
# limitations under the License.
import json
from webob import Response
from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.app.wsgi import route
from ryu.app.wsgi import WSGIApplication
from ryu.app.wsgi import ControllerBase, WSGIApplication, route
from ryu.base import app_manager
from ryu.lib import dpid as dpid_lib
from ryu.topology.api import get_switch, get_link, get_host

218
ryu/app/rest_tunnel.py Normal file
View File

@ -0,0 +1,218 @@
# 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.
"""
Provide a set of REST API for tunnel key management. Used by OpenStack
Ryu plug-in.
- Tunnel key registration for a network
- Manage switches and their ports which are used to establish a tunnel
"""
import json
from webob import Response
from ryu.app import wsgi as app_wsgi
from ryu.app.wsgi import ControllerBase, WSGIApplication
from ryu.base import app_manager
from ryu.controller import network
from ryu.controller import tunnels
import ryu.exception as ryu_exc
from ryu.lib import dpid as dpid_lib
# REST API for tunneling
#
# register tunnel key of this network
# Fail if the key is already registered
# POST /v1.0/tunnels/networks/{network-id}/key/{tunnel_key}
#
# register tunnel key of this network
# Success as nop even if the same key is already registered
# PUT /v1.0/tunnels/networks/{network-id}/key/{tunnel_key}
#
# return allocated tunnel key of this network
# GET /v1.0/tunnels/networks/{network-id}/key
#
# get the ports of dpid that are used for tunneling
# GET /v1.0/tunnels/switches/{dpid}/ports
#
# get the dpid of the other end of tunnel
# GET /v1.0/tunnels/switches/{dpid}/ports/{port-id}/
#
# register the dpid of the other end of tunnel
# Fail if the dpid is already registered
# POST /v1.0/tunnels/switches/{dpid}/ports/{port-id}/{remote_dpid}
#
# register the dpid of the other end of tunnel
# Success as nop even if the dpid is already registered
# PUT /v1.0/tunnels/switches/{dpid}/ports/{port-id}/{remote_dpid}
class TunnelKeyController(ControllerBase):
def __init__(self, req, link, data, **config):
super(TunnelKeyController, self).__init__(req, link, data, **config)
self.tunnels = data
def create(self, _req, network_id, tunnel_key, **_kwargs):
tunnel_key = int(tunnel_key)
try:
self.tunnels.register_key(network_id, tunnel_key)
except (ryu_exc.NetworkAlreadyExist, tunnels.TunnelKeyAlreadyExist):
return Response(status=409)
return Response(status=200)
def update(self, _req, network_id, tunnel_key, **_kwargs):
tunnel_key = int(tunnel_key)
try:
self.tunnels.update_key(network_id, tunnel_key)
except (ryu_exc.NetworkAlreadyExist, tunnels.TunnelKeyAlreadyExist):
return Response(status=409)
return Response(status=200)
def lists(self, _req, network_id, **_kwargs):
try:
tunnel_key = self.tunnels.get_key(network_id)
except tunnels.TunnelKeyNotFound:
return Response(status=404)
body = json.dumps(tunnel_key)
return Response(content_type='application/json', body=body)
def delete(self, _req, network_id, **_kwargs):
try:
self.tunnels.delete_key(network_id)
except (ryu_exc.NetworkNotFound, tunnels.TunnelKeyNotFound):
return Response(status=404)
return Response(status=200)
class TunnelPortController(ControllerBase):
def __init__(self, req, link, data, **config):
super(TunnelPortController, self).__init__(req, link, data, **config)
self.tunnels = data
def create(self, _req, dpid, port_id, remote_dpid, **_kwargs):
dpid = dpid_lib.str_to_dpid(dpid)
port_id = int(port_id)
remote_dpid = dpid_lib.str_to_dpid(remote_dpid)
try:
self.tunnels.register_port(dpid, port_id, remote_dpid)
except ryu_exc.PortAlreadyExist:
return Response(status=409)
return Response(status=200)
def update(self, _req, dpid, port_id, remote_dpid, **_kwargs):
dpid = dpid_lib.str_to_dpid(dpid)
port_id = int(port_id)
remote_dpid = dpid_lib.str_to_dpid(remote_dpid)
try:
self.tunnels.update_port(dpid, port_id, remote_dpid)
except tunnels.RemoteDPIDAlreadyExist:
return Response(status=409)
return Response(status=200)
def lists(self, _req, dpid, **_kwargs):
dpid = dpid_lib.str_to_dpid(dpid)
ports = self.tunnels.list_ports(dpid)
body = json.dumps(ports)
return Response(content_type='application/json', body=body)
def get(self, _req, dpid, port_id, **_kwargs):
dpid = dpid_lib.str_to_dpid(dpid)
port_id = int(port_id)
try:
remote_dpid = self.tunnels.get_remote_dpid(dpid, port_id)
except ryu_exc.PortNotFound:
return Response(status=404)
body = json.dumps(dpid_lib.dpid_to_str(remote_dpid))
return Response(content_type='application/json', body=body)
def delete(self, _req, dpid, port_id, **_kwargs):
dpid = dpid_lib.str_to_dpid(dpid)
port_id = int(port_id)
try:
self.tunnels.delete_port(dpid, port_id)
except ryu_exc.PortNotFound:
return Response(status=404)
return Response(status=200)
class TunnelAPI(app_manager.RyuApp):
_CONTEXTS = {
'network': network.Network,
'tunnels': tunnels.Tunnels,
'wsgi': WSGIApplication
}
def __init__(self, *_args, **kwargs):
super(TunnelAPI, self).__init__()
self.nw = kwargs['network']
self.tunnels = kwargs['tunnels']
wsgi = kwargs['wsgi']
mapper = wsgi.mapper
controller = TunnelKeyController
wsgi.registory[controller.__name__] = self.tunnels
route_name = 'tunnel_key'
uri = '/v1.0/tunnels'
key_uri = uri + '/networks/{network_id}/key'
s = mapper.submapper(controller=controller)
s.connect(route_name, key_uri, action='lists',
conditions=dict(method=['GET', 'HEAD']))
s.connect(route_name, key_uri, action='delete',
conditions=dict(method=['DELETE']))
key_uri += '/{tunnel_key}'
requirements = {route_name: app_wsgi.DIGIT_PATTERN}
s = mapper.submapper(controller=controller, requirements=requirements)
s.connect(route_name, key_uri, action='create',
conditions=dict(method=['POST']))
s.connect(route_name, key_uri, action='update',
conditions=dict(method=['PUT']))
controller = TunnelPortController
wsgi.registory[controller.__name__] = self.tunnels
route_name = 'tunnel_port'
sw_uri = uri + '/switches/{dpid}/ports'
requirements = {'dpid': dpid_lib.DPID_PATTERN}
mapper.connect(route_name, sw_uri, controller=controller,
action='lists', conditions=dict(method=['GET', 'HEAD']),
requirements=requirements)
sw_uri += '/{port_id}'
requirements['port_id'] = app_wsgi.DIGIT_PATTERN
s = mapper.submapper(controller=controller, requirements=requirements)
mapper.connect(route_name, sw_uri, action='get',
conditions=dict(method=['GET', 'HEAD']))
mapper.connect(route_name, sw_uri, action='delete',
conditions=dict(method=['DELETE']))
sw_uri += '/{remote_dpid}'
requirements['remote_dpid'] = dpid_lib.DPID_PATTERN
s = mapper.submapper(controller=controller, requirements=requirements)
mapper.connect(route_name, sw_uri, action='create',
conditions=dict(method=['POST']))
mapper.connect(route_name, sw_uri, action='update',
conditions=dict(method=['PUT']))

File diff suppressed because it is too large Load Diff

351
ryu/app/simple_isolation.py Normal file
View File

@ -0,0 +1,351 @@
# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2011, 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.
"""
MAC address based isolation logic.
"""
import logging
import struct
from ryu.app.rest_nw_id import NW_ID_UNKNOWN, NW_ID_EXTERNAL
from ryu.base import app_manager
from ryu.exception import MacAddressDuplicated
from ryu.exception import PortUnknown
from ryu.controller import dpset
from ryu.controller import mac_to_network
from ryu.controller import mac_to_port
from ryu.controller import network
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import CONFIG_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import nx_match
from ryu.lib.mac import haddr_to_str
from ryu.lib import mac
class SimpleIsolation(app_manager.RyuApp):
_CONTEXTS = {
'network': network.Network,
'dpset': dpset.DPSet,
}
def __init__(self, *args, **kwargs):
super(SimpleIsolation, self).__init__(*args, **kwargs)
self.nw = kwargs['network']
self.dpset = kwargs['dpset']
self.mac2port = mac_to_port.MacToPortTable()
self.mac2net = mac_to_network.MacToNetwork(self.nw)
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
datapath.send_delete_all_flows()
datapath.send_barrier()
self.mac2port.dpid_add(ev.msg.datapath_id)
self.nw.add_datapath(ev.msg)
@staticmethod
def _modflow_and_send_packet(msg, src, dst, actions):
datapath = msg.datapath
ofproto = datapath.ofproto
#
# install flow and then send packet
#
rule = nx_match.ClsRule()
rule.set_in_port(msg.in_port)
rule.set_dl_dst(dst)
rule.set_dl_src(src)
datapath.send_flow_mod(
rule=rule, cookie=0, command=datapath.ofproto.OFPFC_ADD,
idle_timeout=0, hard_timeout=0,
priority=ofproto.OFP_DEFAULT_PRIORITY,
buffer_id=ofproto.OFP_NO_BUFFER, out_port=ofproto.OFPP_NONE,
flags=ofproto.OFPFF_SEND_FLOW_REM, actions=actions)
datapath.send_packet_out(msg.buffer_id, msg.in_port, actions)
def _forward_to_nw_id(self, msg, src, dst, nw_id, out_port):
assert out_port is not None
datapath = msg.datapath
if not self.nw.same_network(datapath.id, nw_id, out_port,
NW_ID_EXTERNAL):
self.logger.debug('packet is blocked src %s dst %s '
'from %d to %d on datapath %d',
haddr_to_str(src), haddr_to_str(dst),
msg.in_port, out_port, datapath.id)
return
self.logger.debug("learned dpid %s in_port %d out_port "
"%d src %s dst %s",
datapath.id, msg.in_port, out_port,
haddr_to_str(src), haddr_to_str(dst))
actions = [datapath.ofproto_parser.OFPActionOutput(out_port)]
self._modflow_and_send_packet(msg, src, dst, actions)
def _flood_to_nw_id(self, msg, src, dst, nw_id):
datapath = msg.datapath
actions = []
self.logger.debug("dpid %s in_port %d src %s dst %s ports %s",
datapath.id, msg.in_port,
haddr_to_str(src), haddr_to_str(dst),
list(self.nw.dpids.get(datapath.id, {}).items()))
for port_no in self.nw.filter_ports(datapath.id, msg.in_port,
nw_id, NW_ID_EXTERNAL):
self.logger.debug("port_no %s", port_no)
actions.append(datapath.ofproto_parser.OFPActionOutput(port_no))
self._modflow_and_send_packet(msg, src, dst, actions)
def _learned_mac_or_flood_to_nw_id(self, msg, src, dst,
dst_nw_id, out_port):
if out_port is not None:
self._forward_to_nw_id(msg, src, dst, dst_nw_id, out_port)
else:
self._flood_to_nw_id(msg, src, dst, dst_nw_id)
def _modflow_and_drop_packet(self, msg, src, dst):
self._modflow_and_send_packet(msg, src, dst, [])
def _drop_packet(self, msg):
datapath = msg.datapath
datapath.send_packet_out(msg.buffer_id, msg.in_port, [])
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
# self.logger.debug('packet in ev %s msg %s', ev, ev.msg)
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
dst, src, _eth_type = struct.unpack_from('!6s6sH', buffer(msg.data), 0)
try:
port_nw_id = self.nw.get_network(datapath.id, msg.in_port)
except PortUnknown:
port_nw_id = NW_ID_UNKNOWN
if port_nw_id != NW_ID_UNKNOWN:
# Here it is assumed that the
# (port <-> network id)/(mac <-> network id) relationship
# is stable once the port is created. The port will be destroyed
# before assigning new network id to the given port.
# This is correct nova-network/nova-compute.
try:
# allow external -> known nw id change
self.mac2net.add_mac(src, port_nw_id, NW_ID_EXTERNAL)
except MacAddressDuplicated:
self.logger.warn('mac address %s is already in use.'
' So (dpid %s, port %s) can not use it',
haddr_to_str(src), datapath.id, msg.in_port)
#
# should we install drop action pro-actively for future?
#
self._drop_packet(msg)
return
old_port = self.mac2port.port_add(datapath.id, msg.in_port, src)
if old_port is not None and old_port != msg.in_port:
# We really overwrite already learned mac address.
# So discard already installed stale flow entry which conflicts
# new port.
rule = nx_match.ClsRule()
rule.set_dl_dst(src)
datapath.send_flow_mod(rule=rule,
cookie=0,
command=ofproto.OFPFC_DELETE,
idle_timeout=0,
hard_timeout=0,
priority=ofproto.OFP_DEFAULT_PRIORITY,
out_port=old_port)
# to make sure the old flow entries are purged.
datapath.send_barrier()
src_nw_id = self.mac2net.get_network(src, NW_ID_UNKNOWN)
dst_nw_id = self.mac2net.get_network(dst, NW_ID_UNKNOWN)
# we handle multicast packet as same as broadcast
broadcast = (dst == mac.BROADCAST) or mac.is_multicast(dst)
out_port = self.mac2port.port_get(datapath.id, dst)
#
# there are several combinations:
# in_port: known nw_id, external, unknown nw,
# src mac: known nw_id, external, unknown nw,
# dst mac: known nw_id, external, unknown nw, and broadcast/multicast
# where known nw_id: is quantum network id
# external: means that these ports are connected to outside
# unknown nw: means that we don't know this port is bounded to
# specific nw_id or external
# broadcast: the destination mac address is broadcast address
# (or multicast address)
#
# Can the following logic be refined/shortened?
#
# When NW_ID_UNKNOWN is found, registering ports might be delayed.
# So just drop only this packet and not install flow entry.
# It is expected that when next packet arrives, the port is registers
# with some network id
if port_nw_id != NW_ID_EXTERNAL and port_nw_id != NW_ID_UNKNOWN:
if broadcast:
# flood to all ports of external or src_nw_id
self._flood_to_nw_id(msg, src, dst, src_nw_id)
elif src_nw_id == NW_ID_EXTERNAL:
self._modflow_and_drop_packet(msg, src, dst)
return
elif src_nw_id == NW_ID_UNKNOWN:
self._drop_packet(msg)
return
else:
# src_nw_id != NW_ID_EXTERNAL and src_nw_id != NW_ID_UNKNOWN:
#
# try learned mac check if the port is net_id
# or
# flood to all ports of external or src_nw_id
self._learned_mac_or_flood_to_nw_id(msg, src, dst,
src_nw_id, out_port)
elif port_nw_id == NW_ID_EXTERNAL:
if src_nw_id != NW_ID_EXTERNAL and src_nw_id != NW_ID_UNKNOWN:
if broadcast:
# flood to all ports of external or src_nw_id
self._flood_to_nw_id(msg, src, dst, src_nw_id)
elif (dst_nw_id != NW_ID_EXTERNAL and
dst_nw_id != NW_ID_UNKNOWN):
if src_nw_id == dst_nw_id:
# try learned mac
# check if the port is external or same net_id
# or
# flood to all ports of external or src_nw_id
self._learned_mac_or_flood_to_nw_id(msg, src, dst,
src_nw_id,
out_port)
else:
# should not occur?
self.logger.debug("should this case happen?")
self._drop_packet(msg)
elif dst_nw_id == NW_ID_EXTERNAL:
# try learned mac
# or
# flood to all ports of external or src_nw_id
self._learned_mac_or_flood_to_nw_id(msg, src, dst,
src_nw_id, out_port)
else:
assert dst_nw_id == NW_ID_UNKNOWN
self.logger.debug("Unknown dst_nw_id")
self._drop_packet(msg)
elif src_nw_id == NW_ID_EXTERNAL:
self._modflow_and_drop_packet(msg, src, dst)
else:
# should not occur?
assert src_nw_id == NW_ID_UNKNOWN
self._drop_packet(msg)
else:
# drop packets
assert port_nw_id == NW_ID_UNKNOWN
self._drop_packet(msg)
# self.logger.debug("Unknown port_nw_id")
def _port_add(self, ev):
#
# delete flows entries that matches with
# dl_dst == broadcast/multicast
# and dl_src = network id if network id of this port is known
# to send broadcast packet to this newly added port.
#
# Openflow v1.0 doesn't support masked match of dl_dst,
# so delete all flow entries. It's inefficient, though.
#
msg = ev.msg
datapath = msg.datapath
datapath.send_delete_all_flows()
datapath.send_barrier()
self.nw.port_added(datapath, msg.desc.port_no)
def _port_del(self, ev):
# free mac addresses associated to this VM port,
# and delete related flow entries for later reuse of mac address
dps_needs_barrier = set()
msg = ev.msg
datapath = msg.datapath
datapath_id = datapath.id
port_no = msg.desc.port_no
rule = nx_match.ClsRule()
rule.set_in_port(port_no)
datapath.send_flow_del(rule=rule, cookie=0)
rule = nx_match.ClsRule()
datapath.send_flow_del(rule=rule, cookie=0, out_port=port_no)
dps_needs_barrier.add(datapath)
try:
port_nw_id = self.nw.get_network(datapath_id, port_no)
except PortUnknown:
# race condition between rest api delete port
# and openflow port deletion ofp_event
pass
else:
if port_nw_id in (NW_ID_UNKNOWN, NW_ID_EXTERNAL):
datapath.send_barrier()
return
for mac_ in self.mac2port.mac_list(datapath_id, port_no):
for (_dpid, dp) in self.dpset.get_all():
if self.mac2port.port_get(dp.id, mac_) is None:
continue
rule = nx_match.ClsRule()
rule.set_dl_src(mac_)
dp.send_flow_del(rule=rule, cookie=0)
rule = nx_match.ClsRule()
rule.set_dl_dst(mac_)
dp.send_flow_del(rule=rule, cookie=0)
dps_needs_barrier.add(dp)
self.mac2port.mac_del(dp.id, mac_)
self.mac2net.del_mac(mac_)
self.nw.port_deleted(datapath.id, port_no)
for dp in dps_needs_barrier:
dp.send_barrier()
@set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
def port_status_handler(self, ev):
msg = ev.msg
reason = msg.reason
ofproto = msg.datapath.ofproto
if reason == ofproto.OFPPR_ADD:
self._port_add(ev)
elif reason == ofproto.OFPPR_DELETE:
self._port_del(ev)
else:
assert reason == ofproto.OFPPR_MODIFY

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 ryu.app import simple_switch_13
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.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

@ -36,12 +36,11 @@ class SimpleSwitch(app_manager.RyuApp):
super(SimpleSwitch, self).__init__(*args, **kwargs)
self.mac_to_port = {}
def add_flow(self, datapath, in_port, dst, src, actions):
def add_flow(self, datapath, in_port, dst, 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))
in_port=in_port, dl_dst=haddr_to_bin(dst))
mod = datapath.ofproto_parser.OFPFlowMod(
datapath=datapath, match=match, cookie=0,
@ -82,7 +81,7 @@ class SimpleSwitch(app_manager.RyuApp):
# 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)
self.add_flow(datapath, msg.in_port, dst, actions)
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:

View File

@ -30,12 +30,11 @@ class SimpleSwitch12(app_manager.RyuApp):
super(SimpleSwitch12, self).__init__(*args, **kwargs)
self.mac_to_port = {}
def add_flow(self, datapath, port, dst, src, actions):
def add_flow(self, datapath, port, dst, actions):
ofproto = datapath.ofproto
match = datapath.ofproto_parser.OFPMatch(in_port=port,
eth_dst=dst,
eth_src=src)
eth_dst=dst)
inst = [datapath.ofproto_parser.OFPInstructionActions(
ofproto.OFPIT_APPLY_ACTIONS, actions)]
@ -81,7 +80,7 @@ class SimpleSwitch12(app_manager.RyuApp):
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
self.add_flow(datapath, in_port, dst, src, actions)
self.add_flow(datapath, in_port, dst, actions)
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:

View File

@ -85,7 +85,7 @@ class SimpleSwitch13(app_manager.RyuApp):
dst = eth.dst
src = eth.src
dpid = format(datapath.id, "d").zfill(16)
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
@ -102,7 +102,7 @@ class SimpleSwitch13(app_manager.RyuApp):
# 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)
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
# 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:

View File

@ -93,7 +93,7 @@ class SimpleSwitch14(app_manager.RyuApp):
# 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)
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
self.add_flow(datapath, 1, match, actions)
data = None

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 ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_5
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.lib.packet import ether_types
class SimpleSwitch15(app_manager.RyuApp):
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,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 ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3
from ryu.lib import igmplib
from ryu.lib.dpid import str_to_dpid
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.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,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 ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3
from ryu.lib import lacplib
from ryu.lib.dpid import str_to_dpid
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.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 ryu.app import simple_switch_13
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.app.wsgi import route
from ryu.app.wsgi import WSGIApplication
from ryu.lib import dpid as dpid_lib
simple_switch_instance_name = 'simple_switch_api_app'
url = '/simpleswitch/mactable/{dpid}'
class SimpleSwitchRest13(simple_switch_13.SimpleSwitch13):
_CONTEXTS = {'wsgi': WSGIApplication}
def __init__(self, *args, **kwargs):
super(SimpleSwitchRest13, self).__init__(*args, **kwargs)
self.switches = {}
wsgi = kwargs['wsgi']
wsgi.register(SimpleSwitchController,
{simple_switch_instance_name: self})
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
super(SimpleSwitchRest13, self).switch_features_handler(ev)
datapath = ev.msg.datapath
self.switches[datapath.id] = datapath
self.mac_to_port.setdefault(datapath.id, {})
def set_mac_to_port(self, dpid, entry):
mac_table = self.mac_to_port.setdefault(dpid, {})
datapath = self.switches.get(dpid)
entry_port = entry['port']
entry_mac = entry['mac']
if datapath is not None:
parser = datapath.ofproto_parser
if entry_port not in mac_table.values():
for mac, port in mac_table.items():
# from known device to new device
actions = [parser.OFPActionOutput(entry_port)]
match = parser.OFPMatch(in_port=port, eth_dst=entry_mac)
self.add_flow(datapath, 1, match, actions)
# from new device to known device
actions = [parser.OFPActionOutput(port)]
match = parser.OFPMatch(in_port=entry_port, eth_dst=mac)
self.add_flow(datapath, 1, match, actions)
mac_table.update({entry_mac: entry_port})
return mac_table
class SimpleSwitchController(ControllerBase):
def __init__(self, req, link, data, **config):
super(SimpleSwitchController, self).__init__(req, link, data, **config)
self.simple_switch_app = data[simple_switch_instance_name]
@route('simpleswitch', url, methods=['GET'],
requirements={'dpid': dpid_lib.DPID_PATTERN})
def list_mac_table(self, req, **kwargs):
simple_switch = self.simple_switch_app
dpid = kwargs['dpid']
if dpid not in simple_switch.mac_to_port:
return Response(status=404)
mac_table = simple_switch.mac_to_port.get(dpid, {})
body = json.dumps(mac_table)
return Response(content_type='application/json', text=body)
@route('simpleswitch', url, methods=['PUT'],
requirements={'dpid': dpid_lib.DPID_PATTERN})
def put_mac_table(self, req, **kwargs):
simple_switch = self.simple_switch_app
dpid = kwargs['dpid']
try:
new_entry = req.json if req.body else {}
except ValueError:
raise Response(status=400)
if dpid not in simple_switch.mac_to_port:
return Response(status=404)
try:
mac_table = simple_switch.set_mac_to_port(dpid, new_entry)
body = json.dumps(mac_table)
return Response(content_type='application/json', text=body)
except Exception as e:
return Response(status=500)

Some files were not shown because too many files have changed in this diff Show More