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 # W0614: Unused import %s from wildcard import
# R0801: Similar lines in %s files # R0801: Similar lines in %s files
disable=C0111,W0511,W0142,E0602,C0103,E1101,R0903,W0614,R0801 disable=C0111,W0511,W0142,E0602,C0103,E1101,R0903,W0614,R0801
msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} output-format=parseable
reports=yes reports=yes
files-output=no 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 Submitting a change
=================== ===================
To send patches to ryu, please make a Send patches to ryu-devel@lists.sourceforge.net. Please don't use 'pull
`pull request <https://github.com/faucetsdn/ryu>`_ on GitHub. 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 https://www.kernel.org/doc/Documentation/SubmittingPatches
unit tests to make sure that they don't break the existing features.
The following command does all for you.
.. 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 fujita@rose:~/git/ryu$ ./run_tests.sh
$ pip install -r tools/test-requires
# 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 unittests when you add new Of course, you are encouraged to add unittests when you add new
features (it's not a must though). features (it's not a must though).
Python version and libraries 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 * standard library + widely used library
on these versions. Basically widely used == OpenStack adopted
As usual there are exceptions. gevents. Or python binding library for other
* standard library + widely used library:
Basically widely used == OpenStack adopted.
As usual there are exceptions. Or python binding library for other
component. component.
Coding style guide Coding style guide
================== ==================
* pep8: * pep8
As python is used, PEP8 is would be hopefully mandatory for As python is used, PEP8 is would be hopefully mandatory for
https://www.python.org/dev/peps/pep-0008/ http://www.python.org/dev/peps/pep-0008/
* pylint:
* pylint
Although pylint is useful for finding bugs, but pylint score not very Although pylint is useful for finding bugs, but pylint score not very
important for now because we're still at early development stage. important for now because we're still at early development stage.
https://www.pylint.org/
* Google python style guide is very helpful: * Google python style guide is very helpful
http://google.github.io/styleguide/pyguide.html http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
* Guidelines derived from Guido's Recommendations: Guidelines derived from Guido's Recommendations
============================= ================= ======== ============================= ================= ========
Type Public Internal Type Public Internal
@ -75,11 +62,10 @@ Coding style guide
Local Variables lower_with_under Local Variables lower_with_under
============================= ================= ======== ============================= ================= ========
* OpenStack Nova style guide: * OpenStack Nova style guide
https://github.com/openstack/nova/blob/master/HACKING.rst 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. 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 used by unit tests. To make patches easier to read,
they are normalized using tools/normalize_json.py. Please re-run 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 What's Ryu
========== ==========
Ryu is a component-based software defined networking framework. 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 easy for developers to create new network management and control
applications. Ryu supports various protocols for managing network applications. Ryu supports various protocols for managing network
devices, such as OpenFlow, Netconf, OF-config, etc. About OpenFlow, 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:: If you prefer to install Ryu from the source code::
% git clone https://github.com/faucetsdn/ryu.git % git clone git://github.com/osrg/ryu.git
% cd ryu; pip install . % 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 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:: After writing your application, just type::
% ryu-manager yourapp.py % ryu-manager yourapp.py
@ -38,33 +36,21 @@ After writing your application, just type::
Optional Requirements 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 - NETCONF requires paramiko
- BGP speaker (SSH console) requires paramiko - BGP speaker (ssh console) requires paramiko
- Zebra protocol service (database) requires SQLAlchemy
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 % pip install lxml
% pip install paramiko
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
Support Support
======= =======
Ryu Official site is `<https://ryu-sdn.org/>`_. Ryu Official site is `<http://osrg.github.io/ryu/>`_.
If you have any If you have any
questions, suggestions, and patches, the mailing list is available at 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: Build-Depends-Indep:
python-eventlet, python-eventlet,
python-lxml, python-lxml,
python-msgpack (>= 0.4.0), python-msgpack (>= 0.3.0),
python-netaddr, python-netaddr,
python-oslo.config (>= 1:1.2.0), python-oslo.config (>= 1:1.2.0),
python-paramiko, python-paramiko,
@ -17,9 +17,9 @@ Build-Depends-Indep:
python-pip, python-pip,
python-pbr python-pbr
Standards-Version: 3.9.5 Standards-Version: 3.9.5
Homepage: https://ryu-sdn.org Homepage: http://osrg.github.io/ryu/
Vcs-Git: git://github.com/faucetsdn/ryu.git Vcs-Git: git://github.com/osrg/ryu.git
Vcs-Browser: https://github.com/faucetsdn/ryu Vcs-Browser: http://github.com/osrg/ryu
XS-Python-Version: >= 2.6 XS-Python-Version: >= 2.6
Package: python-ryu Package: python-ryu
@ -28,7 +28,7 @@ Section: python
Depends: Depends:
python-eventlet, python-eventlet,
python-lxml, python-lxml,
python-msgpack (>= 0.4.0), python-msgpack (>= 0.3.0),
python-netaddr, python-netaddr,
python-oslo.config (>= 1:1.2.0), python-oslo.config (>= 1:1.2.0),
python-paramiko, python-paramiko,

2
debian/copyright vendored
View File

@ -1,6 +1,6 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: ryu Upstream-Name: ryu
Source: http://github.com/faucetsdn/ryu Source: http://github.com/osrg/ryu
Files: * Files: *
Copyright: 2014 Ryu Project Team <ryu-devel@lists.sourceforge.net> 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.rst
app/ofctl_rest.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 .. 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 Ryu applications
================ ================
@ -94,6 +86,42 @@ ryu.app.simple_switch
--------------------- ---------------------
.. automodule:: 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 ryu.topology
------------ ------------
.. automodule:: 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 # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # 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 # 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 # 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 # If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities. # typographically correct entities.
#html_use_smartypants = True #html_use_smartypants = True
# (Deprecated since version 1.6)
# Custom sidebar templates, maps document names to template names. # Custom sidebar templates, maps document names to template names.
#html_sidebars = {} #html_sidebars = {}

View File

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

View File

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

View File

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

View File

@ -9,10 +9,7 @@ Ryu provides some useful library for your network applications.
library_packet.rst library_packet.rst
library_packet_ref.rst library_packet_ref.rst
library_pcap.rst
library_of_config.rst library_of_config.rst
library_bgp_speaker.rst library_bgp_speaker.rst
library_bgp_speaker_ref.rst library_bgp_speaker_ref.rst
library_mrt.rst
library_ovsdb_manager.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 Ryu BGP speaker library helps you to enable your code to speak BGP
protocol. The library supports IPv4, IPv4 MPLS-labeled VPN, IPv6 protocol. The library supports ipv4, ipv4 vpn, and ipv6 vpn address
MPLS-labeled VPN and L2VPN EVPN address families. families.
Example 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 OVSDB Manager library
********************* *********************
Path: ``ryu.services.protocols.ovsdb``
Introduction 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 speaking the OVSDB protocol. This enables your code to perform remote
management of the devices and react to topology changes on them. 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 Example
======= =======
The following logs all new OVSDB connections in "handle_new_ovsdb_connection" The following logs all new OVSDB connections and allows creating a port
and also provides the API "create_port" for creating a port on a bridge. on a bridge.
.. code-block:: python .. code-block:: python
import uuid import uuid
from ryu.base import app_manager 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 api as ovsdb
from ryu.services.protocols.ovsdb import event as ovsdb_event 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) @set_ev_cls(ovsdb_event.EventNewOVSDBConnection)
def handle_new_ovsdb_connection(self, ev): def handle_new_ovsdb_connection(self, ev):
system_id = ev.system_id system_id = ev.system_id
address = ev.client.address self.logger.info('New OVSDB connection from system id %s',
self.logger.info( systemd_id)
'New OVSDB connection from system-id=%s, address=%s',
system_id, address)
# Example: If device has bridge "s1", add port "s1-eth99" def create_port(self, systemd_id, bridge_name, name):
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):
new_iface_uuid = uuid.uuid4() new_iface_uuid = uuid.uuid4()
new_port_uuid = uuid.uuid4() new_port_uuid = uuid.uuid4()
def _create_port(tables, insert):
bridge = ovsdb.row_by_name(self, system_id, bridge_name) bridge = ovsdb.row_by_name(self, system_id, bridge_name)
def _create_port(tables, insert):
iface = insert(tables['Interface'], new_iface_uuid) iface = insert(tables['Interface'], new_iface_uuid)
iface.name = name iface.name = name
iface.type = 'internal' 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.name = name
port.interfaces = [iface] 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) req = ovsdb_event.EventModifyRequest(system_id, _create_port)
rep = self.send_request(req) 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) name, bridge, rep.status)
return None 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 .. automodule:: ryu.lib.packet.stream_parser
:members: :members:
List of the sub-classes: .. autoclass:: ryu.lib.packet.bgp.StreamParser
:members:
- :py:mod:`ryu.lib.packet.bgp.StreamParser`
Protocol Header classes Protocol Header classes
======================= =======================
.. toctree:: .. automodule:: ryu.lib.packet.packet_base
:glob: :members:
library_packet_ref/packet_base .. automodule:: ryu.lib.packet.ethernet
library_packet_ref/* :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 :maxdepth: 3
ofproto_base.rst ofproto_base.rst
ofproto_v1_0_ref.rst
ofproto_v1_2_ref.rst ofproto_v1_2_ref.rst
ofproto_v1_3_ref.rst ofproto_v1_3_ref.rst
ofproto_v1_4_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 .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of12/3-16-ofp_experimenter.packet.json
Port Structures
===============
.. autoclass:: OFPPort
Flow Match Structure Flow Match Structure
==================== ====================

View File

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

View File

@ -487,11 +487,6 @@ Experimenter
.. literalinclude:: ../../ryu/tests/unit/ofproto/json/of14/5-16-ofp_experimenter.packet.json .. literalinclude:: ../../ryu/tests/unit/ofproto/json/of14/5-16-ofp_experimenter.packet.json
Port Structures
===============
.. autoclass:: OFPPort
Flow Match Structure 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 Ryu applications are single-threaded entities which implement
various functionalities in Ryu. Events are messages between them. 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 Besides that, there are some Ryu-internal event sources which
are not Ryu applications. One of examples of such event sources are not Ryu applications. One of examples of such event sources
is OpenFlow controller. is OpenFlow controller.
@ -22,11 +22,11 @@ between Ryu applications.
Each Ryu application has a receive queue for events. Each Ryu application has a receive queue for events.
The queue is FIFO and preserves the order of events. The queue is FIFO and preserves the order of events.
Each Ryu application has a thread for event processing. 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. and calling the appropritate event handler for the event type.
Because the event handler is called in the context of Because the event handler is called in the context of
the event processing thread, it should be careful when blocking. the event processing thread, it should be careful for blocking.
While an event handler is blocked, no further events for I.e. while an event handler is blocked, no further events for
the Ryu application will be processed. the Ryu application will be processed.
There are kinds of events which are used to implement synchronous 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 The OpenFlow controller part of Ryu automatically decodes OpenFlow messages
received from switches and send these events to Ryu applications which received from switches and send these events to Ryu applications which
expressed an interest using ryu.controller.handler.set_ev_cls. 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. See :ref:`ofproto_ref` for more info about OpenFlow messages.
ryu.base.app_manager.RyuApp ryu.base.app_manager.RyuApp
@ -93,87 +103,267 @@ ryu.base.app_manager.RyuApp
See :ref:`api_ref`. 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 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 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 ryu.controller.event.EventRequestBase
===================================== =====================================
.. autoclass:: ryu.controller.event.EventRequestBase The base class for synchronous request for RyuApp.send_request.
ryu.controller.event.EventReplyBase 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 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 ========= ====================================================================
================================================ Attribute Description
========= ====================================================================
.. autoclass:: ryu.controller.ofp_event.EventOFPPortStateChange datapath ryu.controller.controller.Datapath instance of the switch
========= ====================================================================
ryu.controller.dpset.EventDP 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 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 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 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 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 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 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 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 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 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 LINC switch
* run Ryu test_of_config app * 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 Install Erlang environment

View File

@ -4,17 +4,10 @@
Using Ryu Network Operating System with OpenStack as OpenFlow controller 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 Ryu cooperates with OpenStack using Quantum Ryu plugin. The plugin is
available in the official Quantum releases. 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 We described instructions of the installation / configuration of OpenStack
with Ryu, and we provide pre-configured VM image to be able to easily try with Ryu, and we provide pre-configured VM image to be able to easily try
OpenStack with Ryu. OpenStack with Ryu.

View File

@ -5,21 +5,21 @@ The First Application
Whetting Your Appetite Whetting Your Appetite
====================== ======================
If you want to manage network gear (switches, routers, etc) your If you want to manage the network gears (switches, routers, etc) at
own way, you just need to write your own Ryu application. Your application your way, you need to write your Ryu application. Your application
tells Ryu how you want to manage the gear. Then Ryu configures the tells Ryu how you want to manage the gears. Then Ryu configures the
gear by using OpenFlow protocol, etc. 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 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. 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 .. 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): def __init__(self, *args, **kwargs):
super(L2Switch, self).__init__(*args, **kwargs) super(L2Switch, self).__init__(*args, **kwargs)
Ryu applications are just Python scripts so you can save the file with Ryu application is just a Python script so you can save the file with
any name, any extension, and any place you want. Let's name the file any name, extensions, and any place you want. Let's name the file
'l2.py' in your home directory. 'l2.py' at your home directory.
This application does nothing useful yet, however it's a complete Ryu This application does nothing useful yet, however it's a complete Ryu
application. In fact, you can run this Ryu application:: 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 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. 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. the ports.
.. code-block:: python .. code-block:: python
@ -53,11 +53,8 @@ the ports.
from ryu.controller import ofp_event from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_0
class L2Switch(app_manager.RyuApp): class L2Switch(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(L2Switch, self).__init__(*args, **kwargs) super(L2Switch, self).__init__(*args, **kwargs)
@ -69,29 +66,24 @@ the ports.
ofp_parser = dp.ofproto_parser ofp_parser = dp.ofproto_parser
actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)] actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
data = None
if msg.buffer_id == ofp.OFP_NO_BUFFER:
data = msg.data
out = ofp_parser.OFPPacketOut( out = ofp_parser.OFPPacketOut(
datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port, datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port,
actions=actions, data = data) actions=actions)
dp.send_msg(out) dp.send_msg(out)
A new method 'packet_in_handler' is added to the L2Switch class. This is 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 the called when Ryu receives an OpenFlow packet_in message. The trick is
'set_ev_cls' decorator. This decorator tells Ryu when the decorated 'set_ev_cls' decorator. This decorator tells Ryu when the decorated
function should be called. function should be called.
The first argument of the decorator indicates which type of event this The first argument of the decorator indicates an event that makes
function should be called for. As you might expect, every time Ryu gets a function called. As you expect easily, every time Ryu gets a
packet_in message, this function is called. 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 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 argument means this function is called only after the negotiation
completes. completes.
@ -108,24 +100,24 @@ Ready for the second half.
* OFPActionOutput class is used with a packet_out message to specify a * OFPActionOutput class is used with a packet_out message to specify a
switch port that you want to send the packet out of. This switch port that you want to send the packet out of. This
application uses the OFPP_FLOOD flag to indicate that the packet should application need a switch to send out of all the ports so OFPP_FLOOD
be sent out on all ports. constant is used.
* OFPPacketOut class is used to build a packet_out message. * OFPPacketOut class is used to build a packet_out message.
* If you call Datapath class's send_msg method with a OpenFlow 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 Here, you finished implementing your first Ryu application. You are ready to
run a Ryu application that does something useful. 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 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 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 `integrated tests
<https://github.com/faucetsdn/ryu/blob/master/ryu/tests/integrated/>`_ <https://github.com/osrg/ryu/blob/master/ryu/tests/integrated/>`_
directory. directory.

View File

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

View File

@ -12,8 +12,8 @@ usage() {
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment" echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
echo " -c, --coverage Generate coverage report" 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 " -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, --pep8 Just run pep8"
echo " -P, --no-pycodestyle, --no-pep8 Don't run pycodestyle(pep8)" echo " -P, --no-pep8 Don't run pep8"
echo " -l, --pylint Just run pylint" echo " -l, --pylint Just run pylint"
echo " -i, --integrated Run integrated test" echo " -i, --integrated Run integrated test"
echo " -v, --verbose Run verbose pylint analysis" echo " -v, --verbose Run verbose pylint analysis"
@ -31,8 +31,8 @@ process_option() {
-V|--virtual-env) always_venv=1; never_venv=0;; -V|--virtual-env) always_venv=1; never_venv=0;;
-N|--no-virtual-env) always_venv=0; never_venv=1;; -N|--no-virtual-env) always_venv=0; never_venv=1;;
-f|--force) force=1;; -f|--force) force=1;;
-p|--pycodestyle|--pep8) just_pycodestyle=1; never_venv=1; always_venv=0;; -p|--pep8) just_pep8=1; never_venv=1; always_venv=0;;
-P|--no-pycodestyle|--no-pep8) no_pycodestyle=1;; -P|--no-pep8) no_pep8=1;;
-l|--pylint) just_pylint=1;; -l|--pylint) just_pylint=1;;
-i|--integrated) integrated=1;; -i|--integrated) integrated=1;;
-c|--coverage) coverage=1;; -c|--coverage) coverage=1;;
@ -46,8 +46,8 @@ venv=.venv
with_venv=tools/with_venv.sh with_venv=tools/with_venv.sh
always_venv=0 always_venv=0
never_venv=0 never_venv=0
just_pycodestyle=0 just_pep8=0
no_pycodestyle=0 no_pep8=0
just_pylint=0 just_pylint=0
integrated=0 integrated=0
force=0 force=0
@ -103,19 +103,13 @@ run_pylint() {
export PYTHONPATH=$OLD_PYTHONPATH export PYTHONPATH=$OLD_PYTHONPATH
} }
run_pycodestyle() { run_pep8() {
PYCODESTYLE=$(which pycodestyle || which pep8) echo "Running pep8 ..."
if [ -z "${PYCODESTYLE}" ]
then
echo "Please install pycodestyle or pep8"
return 1
fi
echo "Running $(basename ${PYCODESTYLE}) ..."
PYCODESTYLE_OPTIONS="--repeat --show-source" PEP8_OPTIONS="--repeat --show-source"
PYCODESTYLE_INCLUDE="ryu setup*.py" PEP8_INCLUDE="ryu setup*.py"
PYCODESTYLE_LOG=pycodestyle.log PEP8_LOG=pep8.log
${wrapper} ${PYCODESTYLE} $PYCODESTYLE_OPTIONS $PYCODESTYLE_INCLUDE | tee $PYCODESTYLE_LOG ${wrapper} pep8 $PEP8_OPTIONS $PEP8_INCLUDE | tee $PEP8_LOG
} }
run_integrated() { run_integrated() {
@ -167,8 +161,8 @@ if [ $coverage -eq 1 ]; then
${wrapper} coverage erase ${wrapper} coverage erase
fi fi
if [ $just_pycodestyle -eq 1 ]; then if [ $just_pep8 -eq 1 ]; then
run_pycodestyle run_pep8
exit exit
fi fi
if [ $just_pylint -eq 1 ]; then if [ $just_pylint -eq 1 ]; then
@ -183,8 +177,8 @@ fi
run_tests run_tests
RV=$? RV=$?
if [ $no_pycodestyle -eq 0 ]; then if [ $no_pep8 -eq 0 ]; then
run_pycodestyle run_pep8
fi fi
if [ $coverage -eq 1 ]; then if [ $coverage -eq 1 ]; then

View File

@ -14,5 +14,5 @@
# limitations under the License. # limitations under the License.
version_info = (4, 34) version_info = (3, 25)
version = '.'.join(map(str, version_info)) 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 path = "%s/html/" % PATH
self.static_app = DirectoryApp(path) self.static_app = DirectoryApp(path)
@route('topology', '/{filename:[^/]*}') @route('topology', '/{filename:.*}')
def static_handler(self, req, **kwargs): def static_handler(self, req, **kwargs):
if kwargs['filename']: if kwargs['filename']:
req.path_info = kwargs['filename'] req.path_info = kwargs['filename']

View File

@ -16,37 +16,22 @@
# client for ryu.app.ofctl.service # client for ryu.app.ofctl.service
import numbers
from ryu.base import app_manager from ryu.base import app_manager
from . import event from . import event
def get_datapath(app, dpid=None): def get_datapath(app, dpid):
""" """
Get datapath object by dpid. Get datapath object by dpid.
:param app: Client RyuApp instance :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 Returns None on error.
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)
""" """
assert isinstance(dpid, numbers.Integral)
return app.send_request(event.GetDatapathRequest(dpid=dpid))() return app.send_request(event.GetDatapathRequest(dpid=dpid))()
@ -70,17 +55,10 @@ def send_msg(app, msg, reply_cls=None, reply_multi=False):
Example:: Example::
# ...(snip)... import ryu.app.ofctl.api as api
import ryu.app.ofctl.api as ofctl_api
class MyApp(app_manager.RyuApp):
def _my_handler(self, ev):
# ...(snip)...
msg = parser.OFPPortDescStatsRequest(datapath=datapath) msg = parser.OFPPortDescStatsRequest(datapath=datapath)
result = ofctl_api.send_msg( result = api.send_msg(self, msg,
self, msg,
reply_cls=parser.OFPPortDescStatsReply, reply_cls=parser.OFPPortDescStatsReply,
reply_multi=True) reply_multi=True)
""" """

View File

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

View File

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

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. Used by OpenStack Ryu agent.
""" """
import json
from six.moves import http_client 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 ControllerBase
from ryu.app.wsgi import Response
from ryu.base import app_manager from ryu.base import app_manager
from ryu.controller import conf_switch from ryu.controller import conf_switch
from ryu.lib import dpid as dpid_lib 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_key(self, req, dpid, key, **_kwargs):
def _set_val(dpid, key): def _set_val(dpid, key):
try: val = json.loads(req.body)
val = req.json if req.body else {}
except ValueError:
return Response(status=http_client.BAD_REQUEST,
body='invalid syntax %s' % req.body)
self.conf_switch.set_key(dpid, key, val) self.conf_switch.set_key(dpid, key, val)
return None return None

View File

@ -17,8 +17,9 @@
import logging import logging
import json import json
from webob import Response
from ryu.app.wsgi import ControllerBase from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.app.wsgi import WSGIApplication from ryu.app.wsgi import WSGIApplication
from ryu.base import app_manager from ryu.base import app_manager
from ryu.controller import ofp_event from ryu.controller import ofp_event
@ -491,8 +492,8 @@ class FirewallController(ControllerBase):
def _set_rule(self, req, switchid, vlan_id=VLANID_NONE): def _set_rule(self, req, switchid, vlan_id=VLANID_NONE):
try: try:
rule = req.json if req.body else {} rule = json.loads(req.body)
except ValueError: except SyntaxError:
FirewallController._LOGGER.debug('invalid syntax %s', req.body) FirewallController._LOGGER.debug('invalid syntax %s', req.body)
return Response(status=400) return Response(status=400)
@ -515,8 +516,8 @@ class FirewallController(ControllerBase):
def _delete_rule(self, req, switchid, vlan_id=VLANID_NONE): def _delete_rule(self, req, switchid, vlan_id=VLANID_NONE):
try: try:
ruleid = req.json if req.body else {} ruleid = json.loads(req.body)
except ValueError: except SyntaxError:
FirewallController._LOGGER.debug('invalid syntax %s', req.body) FirewallController._LOGGER.debug('invalid syntax %s', req.body)
return Response(status=400) return Response(status=400)
@ -678,7 +679,8 @@ class Firewall(object):
def _set_log_status(self, is_enable, waiters): def _set_log_status(self, is_enable, waiters):
if is_enable: 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.' details = 'Log collection started.'
else: else:
actions = [] actions = []
@ -720,7 +722,7 @@ class Firewall(object):
priority = ARP_FLOW_PRIORITY priority = ARP_FLOW_PRIORITY
match = {REST_DL_TYPE: ether.ETH_TYPE_ARP} match = {REST_DL_TYPE: ether.ETH_TYPE_ARP}
action = {REST_ACTION: REST_ACTION_ALLOW} 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, flow = self._to_of_flow(cookie=cookie, priority=priority,
match=match, actions=actions) match=match, actions=actions)
@ -752,7 +754,7 @@ class Firewall(object):
result = self.get_log_status(waiters) result = self.get_log_status(waiters)
if result[REST_LOG_STATUS] == REST_STATUS_ENABLE: if result[REST_LOG_STATUS] == REST_STATUS_ENABLE:
rest[REST_ACTION] = REST_ACTION_PACKETIN 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, flow = self._to_of_flow(cookie=cookie, priority=priority,
match=match, actions=actions) match=match, actions=actions)
@ -879,7 +881,7 @@ class Firewall(object):
rule = {REST_RULE_ID: ruleid} rule = {REST_RULE_ID: ruleid}
rule.update({REST_PRIORITY: flow[REST_PRIORITY]}) rule.update({REST_PRIORITY: flow[REST_PRIORITY]})
rule.update(Match.to_rest(flow)) rule.update(Match.to_rest(flow))
rule.update(Action.to_rest(flow)) rule.update(Action.to_rest(self.dp, flow))
return rule return rule
@ -1077,17 +1079,19 @@ class Match(object):
class Action(object): class Action(object):
@staticmethod @staticmethod
def to_openflow(rest): def to_openflow(dp, rest):
value = rest.get(REST_ACTION, REST_ACTION_ALLOW) value = rest.get(REST_ACTION, REST_ACTION_ALLOW)
if value == REST_ACTION_ALLOW: if value == REST_ACTION_ALLOW:
out_port = dp.ofproto.OFPP_NORMAL
action = [{'type': 'OUTPUT', action = [{'type': 'OUTPUT',
'port': 'NORMAL'}] 'port': out_port}]
elif value == REST_ACTION_DENY: elif value == REST_ACTION_DENY:
action = [] action = []
elif value == REST_ACTION_PACKETIN: elif value == REST_ACTION_PACKETIN:
out_port = dp.ofproto.OFPP_CONTROLLER
action = [{'type': 'OUTPUT', action = [{'type': 'OUTPUT',
'port': 'CONTROLLER', 'port': out_port,
'max_len': 128}] 'max_len': 128}]
else: else:
raise ValueError('Invalid action type.') raise ValueError('Invalid action type.')
@ -1095,9 +1099,9 @@ class Action(object):
return action return action
@staticmethod @staticmethod
def to_rest(openflow): def to_rest(dp, openflow):
if REST_ACTION in openflow: if REST_ACTION in openflow:
action_allow = 'OUTPUT:NORMAL' action_allow = 'OUTPUT:%d' % dp.ofproto.OFPP_NORMAL
if openflow[REST_ACTION] == [action_allow]: if openflow[REST_ACTION] == [action_allow]:
action = {REST_ACTION: REST_ACTION_ALLOW} action = {REST_ACTION: REST_ACTION_ALLOW}
else: 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 json
import re import re
from webob import Response
from ryu.app import conf_switch_key as cs_key from ryu.app import conf_switch_key as cs_key
from ryu.app.wsgi import ControllerBase from ryu.app.wsgi import ControllerBase, WSGIApplication, route
from ryu.app.wsgi import Response
from ryu.app.wsgi import route
from ryu.app.wsgi import WSGIApplication
from ryu.base import app_manager from ryu.base import app_manager
from ryu.controller import conf_switch from ryu.controller import conf_switch
from ryu.controller import ofp_event from ryu.controller import ofp_event
@ -425,7 +424,6 @@ class QoSController(ControllerBase):
@staticmethod @staticmethod
def delete_ovsdb_addr(dpid): def delete_ovsdb_addr(dpid):
ofs = QoSController._OFS_LIST.get(dpid, None) 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}', @route('qos_switch', BASE_URL + '/queue/{switchid}',
@ -508,8 +506,8 @@ class QoSController(ControllerBase):
def _access_switch(self, req, switchid, vlan_id, func, waiters): def _access_switch(self, req, switchid, vlan_id, func, waiters):
try: try:
rest = req.json if req.body else {} rest = json.loads(req.body) if req.body else {}
except ValueError: except SyntaxError:
QoSController._LOGGER.debug('invalid syntax %s', req.body) QoSController._LOGGER.debug('invalid syntax %s', req.body)
return Response(status=400) return Response(status=400)
@ -558,22 +556,6 @@ class QoS(object):
self.vlan_list[VLANID_NONE] = 0 # for VLAN=None self.vlan_list[VLANID_NONE] = 0 # for VLAN=None
self.dp = dp self.dp = dp
self.version = dp.ofproto.OFP_VERSION 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.queue_list = {}
self.CONF = CONF self.CONF = CONF
self.ovsdb_addr = None self.ovsdb_addr = None
@ -601,22 +583,25 @@ class QoS(object):
self.ofctl.mod_flow_entry(self.dp, flow, cmd) self.ofctl.mod_flow_entry(self.dp, flow, cmd)
def set_ovsdb_addr(self, dpid, ovsdb_addr): 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 old_address = self.ovsdb_addr
if old_address == ovsdb_addr: if old_address == ovsdb_addr:
return return
elif ovsdb_addr is None: if ovsdb_addr is None:
# Determine deleting OVSDB address was requested.
if self.ovs_bridge: if self.ovs_bridge:
self.ovs_bridge.del_controller()
self.ovs_bridge = None self.ovs_bridge = None
return return
self.ovsdb_addr = ovsdb_addr
if self.ovs_bridge is None:
ovs_bridge = bridge.OVSBridge(self.CONF, dpid, ovsdb_addr) ovs_bridge = bridge.OVSBridge(self.CONF, dpid, ovsdb_addr)
self.ovs_bridge = ovs_bridge
try: try:
ovs_bridge.init() ovs_bridge.init()
except: except:
raise ValueError('ovsdb addr is not available.') raise ValueError('ovsdb addr is not available.')
self.ovsdb_addr = ovsdb_addr
self.ovs_bridge = ovs_bridge
def _update_vlan_list(self, vlan_list): def _update_vlan_list(self, vlan_list):
for vlan_id in self.vlan_list.keys(): for vlan_id in self.vlan_list.keys():
@ -679,15 +664,7 @@ class QoS(object):
'details': 'ovs_bridge is not exists'} 'details': 'ovs_bridge is not exists'}
return REST_COMMAND_RESULT, msg return REST_COMMAND_RESULT, msg
port_name = rest.get(REST_PORT_NAME, None) self.queue_list.clear()
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 = {}
queue_type = rest.get(REST_QUEUE_TYPE, 'linux-htb') queue_type = rest.get(REST_QUEUE_TYPE, 'linux-htb')
parent_max_rate = rest.get(REST_QUEUE_MAX_RATE, None) parent_max_rate = rest.get(REST_QUEUE_MAX_RATE, None)
queues = rest.get(REST_QUEUES, []) queues = rest.get(REST_QUEUES, [])
@ -705,9 +682,17 @@ class QoS(object):
config['min-rate'] = min_rate config['min-rate'] = min_rate
if len(config): if len(config):
queue_config.append(config) queue_config.append(config)
queue_list[queue_id] = {'config': config} self.queue_list[queue_id] = {'config': config}
queue_id += 1 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: for port_name in vif_ports:
try: try:
self.ovs_bridge.set_qos(port_name, type=queue_type, self.ovs_bridge.set_qos(port_name, type=queue_type,
@ -715,10 +700,9 @@ class QoS(object):
queues=queue_config) queues=queue_config)
except Exception as msg: except Exception as msg:
raise ValueError(msg) raise ValueError(msg)
self.queue_list[port_name] = queue_list
msg = {'result': 'success', msg = {'result': 'success',
'details': queue_list} 'details': self.queue_list}
return REST_COMMAND_RESULT, msg return REST_COMMAND_RESULT, msg
@ -733,9 +717,9 @@ class QoS(object):
@rest_command @rest_command
def delete_queue(self, rest, vlan_id): def delete_queue(self, rest, vlan_id):
self.queue_list.clear()
if self._delete_queue(): if self._delete_queue():
msg = 'success' msg = 'success'
self.queue_list.clear()
else: else:
msg = 'failure' msg = 'failure'
@ -1143,17 +1127,17 @@ class Match(object):
class Action(object): class Action(object):
@staticmethod @staticmethod
def to_rest(flow): def to_rest(openflow):
if REST_ACTION in flow: if REST_ACTION in openflow:
actions = [] actions = []
for act in flow[REST_ACTION]: for action in openflow[REST_ACTION]:
field_value = re.search(r'SET_FIELD: \{ip_dscp:(\d+)', act) field_value = re.search('SET_FIELD: {ip_dscp:(\d+)', action)
if field_value: if field_value:
actions.append({REST_ACTION_MARK: field_value.group(1)}) 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: if meter_value:
actions.append({REST_ACTION_METER: meter_value.group(1)}) 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: if queue_value:
actions.append({REST_ACTION_QUEUE: queue_value.group(1)}) actions.append({REST_ACTION_QUEUE: queue_value.group(1)})
action = {REST_ACTION: actions} action = {REST_ACTION: actions}

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 struct
import json import json
from webob import Response
from ryu.app.wsgi import ControllerBase from ryu.app.wsgi import ControllerBase
from ryu.app.wsgi import Response
from ryu.app.wsgi import WSGIApplication from ryu.app.wsgi import WSGIApplication
from ryu.base import app_manager from ryu.base import app_manager
from ryu.controller import dpset 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 icmp
from ryu.lib.packet import ipv4 from ryu.lib.packet import ipv4
from ryu.lib.packet import packet from ryu.lib.packet import packet
from ryu.lib.packet import packet_base
from ryu.lib.packet import tcp from ryu.lib.packet import tcp
from ryu.lib.packet import udp from ryu.lib.packet import udp
from ryu.lib.packet import vlan from ryu.lib.packet import vlan
@ -377,45 +376,42 @@ class RouterController(ControllerBase):
@rest_command @rest_command
def get_data(self, req, switch_id, **_kwargs): def get_data(self, req, switch_id, **_kwargs):
return self._access_router(switch_id, VLANID_NONE, return self._access_router(switch_id, VLANID_NONE,
'get_data', req) 'get_data', req.body)
# GET /router/{switch_id}/{vlan_id} # GET /router/{switch_id}/{vlan_id}
@rest_command @rest_command
def get_vlan_data(self, req, switch_id, vlan_id, **_kwargs): def get_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
return self._access_router(switch_id, vlan_id, return self._access_router(switch_id, vlan_id,
'get_data', req) 'get_data', req.body)
# POST /router/{switch_id} # POST /router/{switch_id}
@rest_command @rest_command
def set_data(self, req, switch_id, **_kwargs): def set_data(self, req, switch_id, **_kwargs):
return self._access_router(switch_id, VLANID_NONE, return self._access_router(switch_id, VLANID_NONE,
'set_data', req) 'set_data', req.body)
# POST /router/{switch_id}/{vlan_id} # POST /router/{switch_id}/{vlan_id}
@rest_command @rest_command
def set_vlan_data(self, req, switch_id, vlan_id, **_kwargs): def set_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
return self._access_router(switch_id, vlan_id, return self._access_router(switch_id, vlan_id,
'set_data', req) 'set_data', req.body)
# DELETE /router/{switch_id} # DELETE /router/{switch_id}
@rest_command @rest_command
def delete_data(self, req, switch_id, **_kwargs): def delete_data(self, req, switch_id, **_kwargs):
return self._access_router(switch_id, VLANID_NONE, return self._access_router(switch_id, VLANID_NONE,
'delete_data', req) 'delete_data', req.body)
# DELETE /router/{switch_id}/{vlan_id} # DELETE /router/{switch_id}/{vlan_id}
@rest_command @rest_command
def delete_vlan_data(self, req, switch_id, vlan_id, **_kwargs): def delete_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
return self._access_router(switch_id, vlan_id, 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 = [] rest_message = []
routers = self._get_router(switch_id) routers = self._get_router(switch_id)
try: param = json.loads(rest_param) if rest_param else {}
param = req.json if req.body else {}
except ValueError:
raise SyntaxError('invalid syntax %s', req.body)
for router in routers.values(): for router in routers.values():
function = getattr(router, func) function = getattr(router, func)
data = function(vlan_id, param, self.waiters) data = function(vlan_id, param, self.waiters)
@ -570,8 +566,7 @@ class Router(dict):
# TODO: Packet library convert to string # TODO: Packet library convert to string
# self.logger.debug('Packet in = %s', str(pkt), self.sw_id) # self.logger.debug('Packet in = %s', str(pkt), self.sw_id)
header_list = dict((p.protocol_name, p) header_list = dict((p.protocol_name, p)
for p in pkt.protocols for p in pkt.protocols if type(p) != str)
if isinstance(p, packet_base.PacketBase))
if header_list: if header_list:
# Check vlan-tag # Check vlan-tag
vlan_id = VLANID_NONE vlan_id = VLANID_NONE
@ -1010,14 +1005,14 @@ class VlanRouter(object):
else: else:
if header_list[ARP].opcode == arp.ARP_REQUEST: if header_list[ARP].opcode == arp.ARP_REQUEST:
# ARP request to router port -> send ARP reply # ARP request to router port -> send ARP reply
src_mac = self.port_data[in_port].mac src_mac = header_list[ARP].src_mac
dst_mac = header_list[ARP].src_mac dst_mac = self.port_data[in_port].mac
arp_target_mac = dst_mac arp_target_mac = dst_mac
output = in_port output = in_port
in_port = self.ofctl.dp.ofproto.OFPP_CONTROLLER in_port = self.ofctl.dp.ofproto.OFPP_CONTROLLER
self.ofctl.send_arp(arp.ARP_REPLY, self.vlan_id, 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) arp_target_mac, in_port, output)
log_msg = 'Receive ARP request from [%s] to router port [%s].' log_msg = 'Receive ARP request from [%s] to router port [%s].'
@ -1528,37 +1523,18 @@ class OfCtl(object):
eth = protocol_list[ETHERNET] eth = protocol_list[ETHERNET]
e = ethernet.ethernet(eth.src, eth.dst, ether_proto) e = ethernet.ethernet(eth.src, eth.dst, ether_proto)
ip = protocol_list[IPV4]
if icmp_data is None and msg_data is not None: if icmp_data is None and msg_data is not None:
# RFC 4884 says that we should send "at least 128 octets" ip_datagram = msg_data[offset:]
# 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))
if icmp_type == icmp.ICMP_DEST_UNREACH: 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) data=ip_datagram)
elif icmp_type == icmp.ICMP_TIME_EXCEEDED: 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) data=ip_datagram)
ic = icmp.icmp(icmp_type, icmp_code, csum, data=icmp_data) ic = icmp.icmp(icmp_type, icmp_code, csum, data=icmp_data)
ip = protocol_list[IPV4]
if src_ip is None: if src_ip is None:
src_ip = ip.dst src_ip = ip.dst
ip_total_length = ip.header_length * 4 + ic._MIN_LEN ip_total_length = ip.header_length * 4 + ic._MIN_LEN

View File

@ -14,11 +14,9 @@
# limitations under the License. # limitations under the License.
import json import json
from webob import Response
from ryu.app.wsgi import ControllerBase from ryu.app.wsgi import ControllerBase, WSGIApplication, route
from ryu.app.wsgi import Response
from ryu.app.wsgi import route
from ryu.app.wsgi import WSGIApplication
from ryu.base import app_manager from ryu.base import app_manager
from ryu.lib import dpid as dpid_lib from ryu.lib import dpid as dpid_lib
from ryu.topology.api import get_switch, get_link, get_host 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) super(SimpleSwitch, self).__init__(*args, **kwargs)
self.mac_to_port = {} 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 ofproto = datapath.ofproto
match = datapath.ofproto_parser.OFPMatch( match = datapath.ofproto_parser.OFPMatch(
in_port=in_port, in_port=in_port, dl_dst=haddr_to_bin(dst))
dl_dst=haddr_to_bin(dst), dl_src=haddr_to_bin(src))
mod = datapath.ofproto_parser.OFPFlowMod( mod = datapath.ofproto_parser.OFPFlowMod(
datapath=datapath, match=match, cookie=0, datapath=datapath, match=match, cookie=0,
@ -82,7 +81,7 @@ class SimpleSwitch(app_manager.RyuApp):
# install a flow to avoid packet_in next time # install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD: 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 data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER: 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) super(SimpleSwitch12, self).__init__(*args, **kwargs)
self.mac_to_port = {} self.mac_to_port = {}
def add_flow(self, datapath, port, dst, src, actions): def add_flow(self, datapath, port, dst, actions):
ofproto = datapath.ofproto ofproto = datapath.ofproto
match = datapath.ofproto_parser.OFPMatch(in_port=port, match = datapath.ofproto_parser.OFPMatch(in_port=port,
eth_dst=dst, eth_dst=dst)
eth_src=src)
inst = [datapath.ofproto_parser.OFPInstructionActions( inst = [datapath.ofproto_parser.OFPInstructionActions(
ofproto.OFPIT_APPLY_ACTIONS, actions)] ofproto.OFPIT_APPLY_ACTIONS, actions)]
@ -81,7 +80,7 @@ class SimpleSwitch12(app_manager.RyuApp):
# install a flow to avoid packet_in next time # install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD: 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 data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER: if msg.buffer_id == ofproto.OFP_NO_BUFFER:

View File

@ -85,7 +85,7 @@ class SimpleSwitch13(app_manager.RyuApp):
dst = eth.dst dst = eth.dst
src = eth.src src = eth.src
dpid = format(datapath.id, "d").zfill(16) dpid = datapath.id
self.mac_to_port.setdefault(dpid, {}) self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 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 # install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD: 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 # verify if we have a valid buffer_id, if yes avoid to send both
# flow_mod & packet_out # flow_mod & packet_out
if msg.buffer_id != ofproto.OFP_NO_BUFFER: 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 # install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD: 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) self.add_flow(datapath, 1, match, actions)
data = None 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