mirror of
https://github.com/faucetsdn/ryu.git
synced 2026-05-10 14:56:11 +02:00
Compare commits
No commits in common. "master" and "v3.25" have entirely different histories.
25
.github/workflows/tests-unit.yml
vendored
25
.github/workflows/tests-unit.yml
vendored
@ -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
|
|
||||||
@ -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
|
||||||
|
|||||||
@ -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
|
|
||||||
@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -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
17
.travis.yml
Normal 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
|
||||||
@ -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
|
Of course, you are encouraged to add unittests when you add new
|
||||||
# Also, it is convenient to add settings of your editor or IDE for
|
|
||||||
# applying autopep8 automatically.
|
|
||||||
$ autopep8 --recursive --in-place ryu/
|
|
||||||
|
|
||||||
# Execute unit tests and pycodestyle(pep8)
|
|
||||||
$ ./run_tests.sh
|
|
||||||
|
|
||||||
Of course, you are encouraged to add unit tests when you add new
|
|
||||||
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
|
||||||
|
|||||||
42
README.rst
42
README.rst
@ -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
10
debian/control
vendored
@ -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
2
debian/copyright
vendored
@ -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>
|
||||||
|
|||||||
@ -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
|
|
||||||
|
|||||||
@ -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
@ -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
|
|
||||||
@ -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
|
||||||
|
|||||||
@ -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 = {}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
[parsers]
|
|
||||||
smart_quotes: false
|
|
||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
|
||||||
|
|||||||
@ -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
|
||||||
=======
|
=======
|
||||||
|
|||||||
@ -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
|
|
||||||
@ -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:
|
|
||||||
@ -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]
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
ARP
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.arp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
*****************
|
|
||||||
Packet Base Class
|
|
||||||
*****************
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.packet_base
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
BFD
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.bfd
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
BGP
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.bgp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
BMP
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.bmp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
BPDU
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.bpdu
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
CFM
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.cfm
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
DHCP
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.dhcp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
*****
|
|
||||||
DHCP6
|
|
||||||
*****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.dhcp6
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
********
|
|
||||||
Ethernet
|
|
||||||
********
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.ethernet
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
******
|
|
||||||
Geneve
|
|
||||||
******
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.geneve
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
GRE
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.gre
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
ICMP
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.icmp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
******
|
|
||||||
ICMPv6
|
|
||||||
******
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.icmpv6
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
IGMP
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.igmp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
IPv4
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.ipv4
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
IPv6
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.ipv6
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
LLC
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.llc
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
LLDP
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.lldp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
MPLS
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.mpls
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
********
|
|
||||||
OpenFlow
|
|
||||||
********
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.openflow
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
OSPF
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.ospf
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
PBB
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.pbb
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
SCTP
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.sctp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
Slow
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.slow
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
TCP
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.tcp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
UDP
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.udp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
VLAN
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.vlan
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
VRRP
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.vrrp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
*****
|
|
||||||
VXLAN
|
|
||||||
*****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.vxlan
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
*****
|
|
||||||
Zebra
|
|
||||||
*****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.zebra
|
|
||||||
:members:
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
|
|
||||||
@ -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
|
|
||||||
|
|||||||
@ -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
|
|
||||||
@ -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
|
||||||
====================
|
====================
|
||||||
|
|||||||
@ -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
|
||||||
====================
|
====================
|
||||||
|
|||||||
@ -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
|
||||||
====================
|
====================
|
||||||
|
|||||||
@ -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
|
|
||||||
|
|
||||||
@ -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.
|
||||||
|
=========== ==================================================================
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
pip==20.3.4
|
|
||||||
38
run_tests.sh
38
run_tests.sh
@ -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
|
||||||
|
|||||||
@ -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
286
ryu/app/client.py
Normal 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
|
||||||
@ -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
980
ryu/app/gre_tunnel.py
Normal 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, [])
|
||||||
@ -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']
|
||||||
|
|||||||
@ -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)
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
446
ryu/app/quantum_adapter.py
Normal 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
270
ryu/app/rest.py
Normal 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']))
|
||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
41
ryu/app/rest_nw_id.py
Normal 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'
|
||||||
@ -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
136
ryu/app/rest_quantum.py
Normal 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']))
|
||||||
@ -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
|
||||||
|
|||||||
@ -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
218
ryu/app/rest_tunnel.py
Normal 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']))
|
||||||
1842
ryu/app/rest_vtep.py
1842
ryu/app/rest_vtep.py
File diff suppressed because it is too large
Load Diff
351
ryu/app/simple_isolation.py
Normal file
351
ryu/app/simple_isolation.py
Normal 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
|
||||||
@ -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)
|
|
||||||
@ -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:
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
|
||||||
@ -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)
|
|
||||||
@ -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, {})
|
|
||||||
@ -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
Loading…
x
Reference in New Issue
Block a user