mirror of
https://github.com/faucetsdn/ryu.git
synced 2026-05-09 06:16:10 +02:00
Compare commits
No commits in common. "master" and "v4.1" 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
|
||||
@ -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=py27
|
||||
- TOX_ENV=py34
|
||||
- TOX_ENV=pypy
|
||||
- TOX_ENV=pep8
|
||||
|
||||
install:
|
||||
- "pip install tox"
|
||||
|
||||
script:
|
||||
- NOSE_VERBOSE=0 tox -e $TOX_ENV
|
||||
|
||||
sudo: false
|
||||
@ -5,59 +5,46 @@ How to Get Your Change Into Ryu
|
||||
Submitting a change
|
||||
===================
|
||||
|
||||
To send patches to ryu, please make a
|
||||
`pull request <https://github.com/faucetsdn/ryu>`_ on GitHub.
|
||||
Send patches to ryu-devel@lists.sourceforge.net. Please don't use 'pull
|
||||
request' on github. We expect you to send a patch in Linux kernel
|
||||
development style. If you are not familiar with it, please read the
|
||||
following document:
|
||||
|
||||
Please check your changes with autopep8, pycodestyle(pep8) and running
|
||||
unit tests to make sure that they don't break the existing features.
|
||||
The following command does all for you.
|
||||
https://www.kernel.org/doc/Documentation/SubmittingPatches
|
||||
|
||||
.. code-block:: bash
|
||||
Please check your changes with pep8 and run unittests to make sure
|
||||
that they don't break the existing features. The following command
|
||||
does both for you:
|
||||
|
||||
# Install dependencies of tests
|
||||
$ pip install -r tools/test-requires
|
||||
fujita@rose:~/git/ryu$ ./run_tests.sh
|
||||
|
||||
# Execute autopep8
|
||||
# Also, it is convenient to add settings of your editor or IDE for
|
||||
# applying autopep8 automatically.
|
||||
$ autopep8 --recursive --in-place ryu/
|
||||
|
||||
# Execute unit tests and pycodestyle(pep8)
|
||||
$ ./run_tests.sh
|
||||
|
||||
Of course, you are encouraged to add unit tests when you add new
|
||||
Of course, you are encouraged to add unittests when you add new
|
||||
features (it's not a must though).
|
||||
|
||||
Python version and libraries
|
||||
============================
|
||||
* Python 3.5, 3.6, 3.7, 3.8, 3.9:
|
||||
* Python 2.6+
|
||||
As RHEL 6 adopted python 2.6, features only for 2.7+ should be avoided.
|
||||
|
||||
Ryu supports multiple Python versions. CI tests on GitHub Actions is running
|
||||
on these versions.
|
||||
|
||||
* standard library + widely used library:
|
||||
|
||||
Basically widely used == OpenStack adopted.
|
||||
As usual there are exceptions. Or python binding library for other
|
||||
* standard library + widely used library
|
||||
Basically widely used == OpenStack adopted
|
||||
As usual there are exceptions. gevents. Or python binding library for other
|
||||
component.
|
||||
|
||||
Coding style guide
|
||||
==================
|
||||
* pep8:
|
||||
|
||||
* pep8
|
||||
As python is used, PEP8 is would be hopefully mandatory for
|
||||
https://www.python.org/dev/peps/pep-0008/
|
||||
|
||||
* pylint:
|
||||
http://www.python.org/dev/peps/pep-0008/
|
||||
|
||||
* pylint
|
||||
Although pylint is useful for finding bugs, but pylint score not very
|
||||
important for now because we're still at early development stage.
|
||||
https://www.pylint.org/
|
||||
|
||||
* Google python style guide is very helpful:
|
||||
http://google.github.io/styleguide/pyguide.html
|
||||
* Google python style guide is very helpful
|
||||
http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
|
||||
|
||||
* Guidelines derived from Guido's Recommendations:
|
||||
Guidelines derived from Guido's Recommendations
|
||||
|
||||
============================= ================= ========
|
||||
Type Public Internal
|
||||
@ -75,11 +62,10 @@ Coding style guide
|
||||
Local Variables lower_with_under
|
||||
============================= ================= ========
|
||||
|
||||
* OpenStack Nova style guide:
|
||||
* OpenStack Nova style guide
|
||||
https://github.com/openstack/nova/blob/master/HACKING.rst
|
||||
|
||||
* JSON files:
|
||||
|
||||
* JSON files
|
||||
Ryu source tree has JSON files under ryu/tests/unit/ofproto/json.
|
||||
They are used by unit tests. To make patches easier to read,
|
||||
they are normalized using tools/normalize_json.py. Please re-run
|
||||
|
||||
39
README.rst
39
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
|
||||
==========
|
||||
Ryu is a component-based software defined networking framework.
|
||||
|
||||
Ryu provides software components with well defined API's that make it
|
||||
Ryu provides software components with well defined API that make it
|
||||
easy for developers to create new network management and control
|
||||
applications. Ryu supports various protocols for managing network
|
||||
devices, such as OpenFlow, Netconf, OF-config, etc. About OpenFlow,
|
||||
@ -25,11 +20,11 @@ Installing Ryu is quite easy::
|
||||
|
||||
If you prefer to install Ryu from the source code::
|
||||
|
||||
% git clone https://github.com/faucetsdn/ryu.git
|
||||
% cd ryu; pip install .
|
||||
% git clone git://github.com/osrg/ryu.git
|
||||
% cd ryu; python ./setup.py install
|
||||
|
||||
If you want to write your Ryu application, have a look at
|
||||
`Writing ryu application <http://ryu.readthedocs.io/en/latest/writing_ryu_app.html>`_ document.
|
||||
`Writing ryu application <http://ryu.readthedocs.org/en/latest/writing_ryu_app.html>`_ document.
|
||||
After writing your application, just type::
|
||||
|
||||
% ryu-manager yourapp.py
|
||||
@ -38,33 +33,21 @@ After writing your application, just type::
|
||||
Optional Requirements
|
||||
=====================
|
||||
|
||||
Some functions of ryu require extra packages:
|
||||
Some functionalities of ryu requires extra packages:
|
||||
|
||||
- OF-Config requires lxml and ncclient
|
||||
- OF-Config requires lxml
|
||||
- NETCONF requires paramiko
|
||||
- BGP speaker (SSH console) requires paramiko
|
||||
- Zebra protocol service (database) requires SQLAlchemy
|
||||
- BGP speaker (ssh console) requires paramiko
|
||||
|
||||
If you want to use these functions, please install the requirements::
|
||||
If you want to use the functionalities, please install requirements::
|
||||
|
||||
% pip install -r tools/optional-requires
|
||||
|
||||
Please refer to tools/optional-requires for details.
|
||||
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
If you got some error messages at the installation stage, please confirm
|
||||
dependencies for building the required Python packages.
|
||||
|
||||
On Ubuntu(16.04 LTS or later)::
|
||||
|
||||
% apt install gcc python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev zlib1g-dev
|
||||
% pip install lxml
|
||||
% pip install paramiko
|
||||
|
||||
|
||||
Support
|
||||
=======
|
||||
Ryu Official site is `<https://ryu-sdn.org/>`_.
|
||||
Ryu Official site is `<http://osrg.github.io/ryu/>`_.
|
||||
|
||||
If you have any
|
||||
questions, suggestions, and patches, the mailing list is available at
|
||||
|
||||
10
debian/control
vendored
10
debian/control
vendored
@ -6,7 +6,7 @@ Build-Depends: debhelper (>= 9.0.0), python-all (>= 2.6), python-sphinx
|
||||
Build-Depends-Indep:
|
||||
python-eventlet,
|
||||
python-lxml,
|
||||
python-msgpack (>= 0.4.0),
|
||||
python-msgpack (>= 0.3.0),
|
||||
python-netaddr,
|
||||
python-oslo.config (>= 1:1.2.0),
|
||||
python-paramiko,
|
||||
@ -17,9 +17,9 @@ Build-Depends-Indep:
|
||||
python-pip,
|
||||
python-pbr
|
||||
Standards-Version: 3.9.5
|
||||
Homepage: https://ryu-sdn.org
|
||||
Vcs-Git: git://github.com/faucetsdn/ryu.git
|
||||
Vcs-Browser: https://github.com/faucetsdn/ryu
|
||||
Homepage: http://osrg.github.io/ryu/
|
||||
Vcs-Git: git://github.com/osrg/ryu.git
|
||||
Vcs-Browser: http://github.com/osrg/ryu
|
||||
XS-Python-Version: >= 2.6
|
||||
|
||||
Package: python-ryu
|
||||
@ -28,7 +28,7 @@ Section: python
|
||||
Depends:
|
||||
python-eventlet,
|
||||
python-lxml,
|
||||
python-msgpack (>= 0.4.0),
|
||||
python-msgpack (>= 0.3.0),
|
||||
python-netaddr,
|
||||
python-oslo.config (>= 1:1.2.0),
|
||||
python-paramiko,
|
||||
|
||||
2
debian/copyright
vendored
2
debian/copyright
vendored
@ -1,6 +1,6 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: ryu
|
||||
Source: http://github.com/faucetsdn/ryu
|
||||
Source: http://github.com/osrg/ryu
|
||||
|
||||
Files: *
|
||||
Copyright: 2014 Ryu Project Team <ryu-devel@lists.sourceforge.net>
|
||||
|
||||
@ -11,5 +11,3 @@ Others provide some functionalities to other Ryu applications.
|
||||
|
||||
app/ofctl.rst
|
||||
app/ofctl_rest.rst
|
||||
app/rest_vtep.rst
|
||||
app/bgp_application.rst
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
**************************************
|
||||
ryu.services.protocols.bgp.application
|
||||
**************************************
|
||||
|
||||
.. automodule:: ryu.services.protocols.bgp.application
|
||||
:members:
|
||||
@ -6,7 +6,7 @@ ryu.app.ofctl_rest provides REST APIs for retrieving the switch stats
|
||||
and Updating the switch stats.
|
||||
This application helps you debug your application and get various statistics.
|
||||
|
||||
This application supports OpenFlow version 1.0, 1.2, 1.3, 1.4 and 1.5.
|
||||
This application supports OpenFlow version 1.0, 1.2, 1.3 and 1.4.
|
||||
|
||||
.. contents::
|
||||
:depth: 3
|
||||
@ -248,17 +248,8 @@ Get flows stats filtered by fields
|
||||
cookie Require matching entries to contain this cookie value (int) 1 0
|
||||
cookie_mask Mask used to restrict the cookie bits that must match (int) 1 0
|
||||
match Fields to match (dict) {"in_port": 1} {} #wildcarded
|
||||
priority Priority of the entry (int) (See Note) 11111 #wildcarded
|
||||
============ ================================================================== =============== ===============
|
||||
|
||||
.. NOTE::
|
||||
|
||||
OpenFlow Spec does not allow to filter flow entries by priority,
|
||||
but when with a large amount of flow entries, filtering by priority
|
||||
is convenient to get statistics efficiently.
|
||||
So, this app provides priority field for filtering.
|
||||
|
||||
|
||||
Response message body:
|
||||
The same as :ref:`get-all-flows-stats`
|
||||
|
||||
@ -898,25 +889,13 @@ Get ports description
|
||||
|
||||
Get ports description of the switch which specified with Datapath ID in URI.
|
||||
|
||||
Usage(OpenFlow1.4 or earlier):
|
||||
Usage:
|
||||
|
||||
======= =======================
|
||||
Method GET
|
||||
URI /stats/portdesc/<dpid>
|
||||
======= =======================
|
||||
|
||||
Usage(OpenFlow1.5 or later):
|
||||
|
||||
======= ==================================
|
||||
Method GET
|
||||
URI /stats/portdesc/<dpid>/[<port>]
|
||||
======= ==================================
|
||||
|
||||
.. NOTE::
|
||||
|
||||
Specification of port number is optional.
|
||||
|
||||
|
||||
Response message body(OpenFlow1.3 or earlier):
|
||||
|
||||
============== ====================================== ====================
|
||||
@ -1178,15 +1157,10 @@ Get queues config
|
||||
|
||||
Usage:
|
||||
|
||||
======= ==================================
|
||||
======= ================================
|
||||
Method GET
|
||||
URI /stats/queueconfig/<dpid>/[<port>]
|
||||
======= ==================================
|
||||
|
||||
.. NOTE::
|
||||
|
||||
Specification of port number is optional.
|
||||
|
||||
URI /stats/queueconfig/<dpid>/<port>
|
||||
======= ================================
|
||||
|
||||
.. CAUTION::
|
||||
|
||||
@ -1262,20 +1236,10 @@ Get queues description
|
||||
|
||||
Usage:
|
||||
|
||||
======= =============================================
|
||||
======= =========================================
|
||||
Method GET
|
||||
URI /stats/queuedesc/<dpid>[/<port>/[<queue_id>]]
|
||||
======= =============================================
|
||||
|
||||
.. NOTE::
|
||||
|
||||
Specification of port number and queue id are optional.
|
||||
|
||||
If you want to omitting the port number and setting the queue id,
|
||||
please specify the keyword "ALL" to the port number.
|
||||
|
||||
e.g. GET http://localhost:8080/stats/queuedesc/1/ALL/1
|
||||
|
||||
URI /stats/queuedesc/<dpid>/<port>/<queue_id>
|
||||
======= =========================================
|
||||
|
||||
.. CAUTION::
|
||||
|
||||
@ -1406,25 +1370,13 @@ Get group description stats
|
||||
|
||||
Get group description stats of the switch which specified with Datapath ID in URI.
|
||||
|
||||
Usage(Openflow1.4 or earlier):
|
||||
Usage:
|
||||
|
||||
======= ========================
|
||||
Method GET
|
||||
URI /stats/groupdesc/<dpid>
|
||||
======= ========================
|
||||
|
||||
Usage(Openflow1.5 or later):
|
||||
|
||||
======= ====================================
|
||||
Method GET
|
||||
URI /stats/groupdesc/<dpid>/[<group_id>]
|
||||
======= ====================================
|
||||
|
||||
.. NOTE::
|
||||
|
||||
Specification of group id is optional.
|
||||
|
||||
|
||||
Response message body(Openflow1.3 or earlier):
|
||||
|
||||
=============== ======================================================= =============
|
||||
@ -1673,33 +1625,17 @@ Get meters stats
|
||||
.. _get-meter-config-stats:
|
||||
|
||||
Get meter config stats
|
||||
----------------------
|
||||
Get meter description stats
|
||||
---------------------------
|
||||
------------------------
|
||||
|
||||
Get meter config stats of the switch which specified with Datapath ID in URI.
|
||||
|
||||
.. CAUTION::
|
||||
|
||||
This message has been renamed in openflow 1.5.
|
||||
If Openflow 1.4 or earlier is in use, please used as Get meter description stats.
|
||||
If Openflow 1.5 or later is in use, please used as Get meter description stats.
|
||||
|
||||
|
||||
Usage(Openflow1.4 or earlier):
|
||||
Usage:
|
||||
|
||||
======= ======================================
|
||||
Method GET
|
||||
URI /stats/meterconfig/<dpid>[/<meter_id>]
|
||||
======= ======================================
|
||||
|
||||
Usage(Openflow1.5 or later):
|
||||
|
||||
======= ======================================
|
||||
Method GET
|
||||
URI /stats/meterdesc/<dpid>[/<meter_id>]
|
||||
======= ======================================
|
||||
|
||||
.. NOTE::
|
||||
|
||||
Specification of meter id is optional.
|
||||
@ -1794,72 +1730,6 @@ Get meter features stats
|
||||
}
|
||||
|
||||
|
||||
Get role
|
||||
--------
|
||||
|
||||
Get the current role of the controller from the switch.
|
||||
|
||||
Usage:
|
||||
|
||||
======= =========================
|
||||
Method GET
|
||||
URI /stats/role/<dpid>
|
||||
======= =========================
|
||||
|
||||
Response message body(Openflow1.4 or earlier):
|
||||
|
||||
============= ============================= =========
|
||||
Attribute Description Example
|
||||
============= ============================= =========
|
||||
dpid Datapath ID 1
|
||||
role One of OFPCR_ROLE_* "EQUAL"
|
||||
generation_id Master Election Generation Id 0
|
||||
============= ============================= =========
|
||||
|
||||
Response message body(Openflow1.5 or later):
|
||||
|
||||
============= ============================= =========
|
||||
Attribute Description Example
|
||||
============= ============================= =========
|
||||
dpid Datapath ID 1
|
||||
role One of OFPCR_ROLE_* "EQUAL"
|
||||
short_id ID number for the controller 0
|
||||
generation_id Master Election Generation Id 0
|
||||
============= ============================= =========
|
||||
|
||||
Example of use::
|
||||
|
||||
$ curl -X GET http://localhost:8080/stats/role/1
|
||||
|
||||
Response (Openflow1.4 or earlier):
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
"1": [
|
||||
{
|
||||
"generation_id": 0,
|
||||
"role": "EQUAL"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Response (Openflow1.5 or later):
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
"1": [
|
||||
{
|
||||
"generation_id": 0,
|
||||
"role": "EQUAL",
|
||||
"short_id": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Update the switch stats
|
||||
=======================
|
||||
|
||||
@ -1895,9 +1765,9 @@ Add a flow entry
|
||||
|
||||
Request message body(Openflow1.4 or later):
|
||||
|
||||
============= ===================================================== ================================ ===============
|
||||
============= ===================================================== =============================== ===============
|
||||
Attribute Description Example Default
|
||||
============= ===================================================== ================================ ===============
|
||||
============= ===================================================== =============================== ===============
|
||||
dpid Datapath ID (int) 1 (Mandatory)
|
||||
cookie Opaque controller-issued identifier (int) 1 0
|
||||
cookie_mask Mask used to restrict the cookie bits (int) 1 0
|
||||
@ -2587,33 +2457,6 @@ Delete a meter entry
|
||||
"meter_id": 1
|
||||
}' http://localhost:8080/stats/meterentry/delete
|
||||
|
||||
Modify role
|
||||
--------------------
|
||||
|
||||
modify the role of the switch.
|
||||
|
||||
Usage:
|
||||
|
||||
======= =========================
|
||||
Method POST
|
||||
URI /stats/role
|
||||
======= =========================
|
||||
|
||||
Request message body:
|
||||
|
||||
=========== ============================ ========= =================
|
||||
Attribute Description Example Default
|
||||
=========== ============================ ========= =================
|
||||
dpid Datapath ID (int) 1 (Mandatory)
|
||||
role One of OFPCR_ROLE_*(string) "MASTER" OFPCR_ROLE_EQUAL
|
||||
=========== ============================ ========= =================
|
||||
|
||||
Example of use::
|
||||
|
||||
$ curl -X POST -d '{
|
||||
"dpid": 1,
|
||||
"role": "MASTER"
|
||||
}' http://localhost:8080/stats/role
|
||||
|
||||
Support for experimenter multipart
|
||||
==================================
|
||||
@ -2657,7 +2500,7 @@ Send a experimenter message
|
||||
.. _description-of-match-and-actions:
|
||||
|
||||
Reference: Description of Match and Actions
|
||||
===========================================
|
||||
============================================
|
||||
|
||||
Description of Match on request messages
|
||||
----------------------------------------
|
||||
@ -2697,15 +2540,26 @@ Description of Match on request messages
|
||||
=============== ================================================== =======================================================================================================
|
||||
in_port Switch input port (int) {"in_port": 7}
|
||||
in_phy_port Switch physical input port (int) {"in_phy_port": 5, "in_port": 3}
|
||||
metadata Metadata passed between tables (int or string) {"metadata": 12345} or {"metadata": "0x1212/0xffff"}
|
||||
metadata Metadata passed between tables (int or string) {"metadata": 12345}
|
||||
|
||||
| {"metadata": "0x1212/0xffff"}
|
||||
dl_dst Ethernet destination address (string) {"dl_dst": "aa:bb:cc:11:22:33/00:00:00:00:ff:ff"}
|
||||
dl_src Ethernet source address (string) {"dl_src": "aa:bb:cc:11:22:33"}
|
||||
eth_dst Ethernet destination address (string) {"eth_dst": "aa:bb:cc:11:22:33/00:00:00:00:ff:ff"}
|
||||
eth_src Ethernet source address (string) {"eth_src": "aa:bb:cc:11:22:33"}
|
||||
dl_type Ethernet frame type (int) {"dl_type": 123}
|
||||
eth_type Ethernet frame type (int) {"eth_type": 2048}
|
||||
dl_vlan VLAN id (int or string) See :ref:`example-of-vlan-id-match-field`
|
||||
vlan_vid VLAN id (int or string) See :ref:`example-of-vlan-id-match-field`
|
||||
vlan_pcp VLAN priority (int) {"vlan_pcp": 3, "vlan_vid": 3}
|
||||
ip_dscp IP DSCP (6 bits in ToS field) (int) {"ip_dscp": 3, "eth_type": 2048}
|
||||
ip_ecn IP ECN (2 bits in ToS field) (int) {"ip_ecn": 0, "eth_type": 34525}
|
||||
nw_proto IP protocol (int) {"nw_proto": 5, "eth_type": 2048}
|
||||
ip_proto IP protocol (int) {"ip_proto": 5, "eth_type": 34525}
|
||||
tp_src Transport layer source port (int) {"tp_src": 1, "ip_proto": 6, "eth_type": 2048}
|
||||
tp_dst Transport layer destination port (int) {"tp_dst": 2, "ip_proto": 6, "eth_type": 2048}
|
||||
nw_src IPv4 source address (string) {"nw_src": "192.168.0.1", "eth_type": 2048}
|
||||
nw_dst IPv4 destination address (string) {"nw_dst": "192.168.0.1/24", "eth_type": 2048}
|
||||
ipv4_src IPv4 source address (string) {"ipv4_src": "192.168.0.1", "eth_type": 2048}
|
||||
ipv4_dst IPv4 destination address (string) {"ipv4_dst": "192.168.10.10/255.255.255.0", "eth_type": 2048}
|
||||
tcp_src TCP source port (int) {"tcp_src": 3, "ip_proto": 6, "eth_type": 2048}
|
||||
@ -2732,21 +2586,15 @@ Description of Match on request messages
|
||||
mpls_label MPLS label (int) {"mpls_label": 3, "eth_type": 34888}
|
||||
mpls_tc MPLS Traffic Class (int) {"mpls_tc": 2, "eth_type": 34888}
|
||||
mpls_bos MPLS BoS bit (int) {"mpls_bos": 1, "eth_type": 34888}
|
||||
(Openflow1.3+)
|
||||
pbb_isid PBB I-SID (int or string) {"pbb_isid": 5, "eth_type": 35047} or{"pbb_isid": "0x05/0xff", "eth_type": 35047}
|
||||
(Openflow1.3+)
|
||||
tunnel_id Logical Port Metadata (int or string) {"tunnel_id": 7} or {"tunnel_id": "0x07/0xff"}
|
||||
(Openflow1.3+)
|
||||
ipv6_exthdr IPv6 Extension Header pseudo-field (int or string) {"ipv6_exthdr": 3, "eth_type": 34525} or {"ipv6_exthdr": "0x40/0x1F0", "eth_type": 34525}
|
||||
(Openflow1.3+)
|
||||
pbb_uca PBB UCA hander field(int) {"pbb_uca": 1, "eth_type": 35047}
|
||||
(Openflow1.4+)
|
||||
tcp_flags TCP flags(int) {"tcp_flags": 2, "ip_proto": 6, "eth_type": 2048}
|
||||
(Openflow1.5+)
|
||||
actset_output Output port from action set metadata(int) {"actset_output": 3}
|
||||
(Openflow1.5+)
|
||||
packet_type Packet type value(int) {"packet_type": [1, 2048]}
|
||||
(Openflow1.5+)
|
||||
pbb_isid PBB I-SID (int or string) {"pbb_isid": 5, "eth_type": 35047}
|
||||
|
||||
| {"pbb_isid": "0x05/0xff", "eth_type": 35047}
|
||||
tunnel_id Logical Port Metadata (int or string) {"tunnel_id": 7}
|
||||
|
||||
| {"tunnel_id": "0x07/0xff"}
|
||||
ipv6_exthdr IPv6 Extension Header pseudo-field (int or string) {"ipv6_exthdr": 3, "eth_type": 34525}
|
||||
|
||||
| {"ipv6_exthdr": "0x40/0x1F0", "eth_type": 34525}
|
||||
=============== ================================================== =======================================================================================================
|
||||
|
||||
.. NOTE::
|
||||
@ -2878,9 +2726,9 @@ Description of Actions on request messages
|
||||
|
||||
List of Actions (OpenFlow1.2 or later):
|
||||
|
||||
=============== ============================================================================ ========================================================================================================================
|
||||
=============== ============================================================================ ==================================================================
|
||||
Actions Description Example
|
||||
=============== ============================================================================ ========================================================================================================================
|
||||
=============== ============================================================================ ==================================================================
|
||||
OUTPUT Output packet from "port" {"type": "OUTPUT", "port": 3}
|
||||
COPY_TTL_OUT Copy TTL outwards {"type": "COPY_TTL_OUT"}
|
||||
COPY_TTL_IN Copy TTL inwards {"type": "COPY_TTL_IN"}
|
||||
@ -2897,24 +2745,15 @@ Description of Actions on request messages
|
||||
SET_FIELD Set a "field" using "value" See :ref:`example-of-set-field-action`
|
||||
(The set of keywords available for "field" is the same as match field)
|
||||
PUSH_PBB Push a new PBB service tag with "ethertype" {"type": "PUSH_PBB", "ethertype": 35047}
|
||||
(Openflow1.3+)
|
||||
POP_PBB Pop the outer PBB service tag {"type": "POP_PBB"}
|
||||
(Openflow1.3+)
|
||||
COPY_FIELD Copy value between header and register {"type": "COPY_FIELD", "n_bits": 32, "src_offset": 1, "dst_offset": 2, "src_oxm_id": "eth_src", "dst_oxm_id": "eth_dst"}
|
||||
(Openflow1.5+)
|
||||
METER Apply meter identified by "meter_id" {"type": "METER", "meter_id": 3}
|
||||
(Openflow1.5+)
|
||||
EXPERIMENTER Extensible action for the experimenter {"type": "EXPERIMENTER", "experimenter": 101, "data": "AAECAwQFBgc=", "data_type": "base64"}
|
||||
(Set "base64" or "ascii" to "data_type" field)
|
||||
GOTO_TABLE (Instruction) Setup the next table identified by "table_id" {"type": "GOTO_TABLE", "table_id": 8}
|
||||
WRITE_METADATA (Instruction) Setup the metadata field using "metadata" and "metadata_mask" {"type": "WRITE_METADATA", "metadata": 0x3, "metadata_mask": 0x3}
|
||||
METER (Instruction) Apply meter identified by "meter_id" {"type": "METER", "meter_id": 3}
|
||||
(deprecated in Openflow1.5)
|
||||
WRITE_ACTIONS (Instruction) Write the action(s) onto the datapath action set {"type": "WRITE_ACTIONS", actions":[{"type":"POP_VLAN",},{ "type":"OUTPUT", "port": 2}]}
|
||||
CLEAR_ACTIONS (Instruction) Clears all actions from the datapath action set {"type": "CLEAR_ACTIONS"}
|
||||
=============== ============================================================================ ========================================================================================================================
|
||||
|
||||
|
||||
=============== ============================================================================ ==================================================================
|
||||
|
||||
.. _example-of-set-field-action:
|
||||
|
||||
@ -2928,20 +2767,19 @@ Example of set-field action
|
||||
"match":{
|
||||
"dl_type": "0x8000"
|
||||
},
|
||||
"actions":[
|
||||
{
|
||||
"type": "PUSH_VLAN", # Push a new VLAN tag if a input frame is non-VLAN-tagged
|
||||
"ethertype": 33024 # Ethertype 0x8100(=33024): IEEE 802.1Q VLAN-tagged frame
|
||||
},
|
||||
{
|
||||
"type": "SET_FIELD",
|
||||
"field": "vlan_vid", # Set VLAN ID
|
||||
"value": 4102 # Describe sum of vlan_id(e.g. 6) | OFPVID_PRESENT(0x1000=4096)
|
||||
},
|
||||
{
|
||||
"type": "OUTPUT",
|
||||
"port": 2
|
||||
}
|
||||
]
|
||||
"actions":[
|
||||
{
|
||||
"type": "PUSH_VLAN", # Push a new VLAN tag if a input frame is non-VLAN-tagged
|
||||
"ethertype": 33024 # Ethertype 0x8100(=33024): IEEE 802.1Q VLAN-tagged frame
|
||||
},
|
||||
{
|
||||
"type": "SET_FIELD",
|
||||
"field": "vlan_vid", # Set VLAN ID
|
||||
"value": 4102 # Describe sum of vlan_id(e.g. 6) | OFPVID_PRESENT(0x1000=4096)
|
||||
},
|
||||
{
|
||||
"type": "OUTPUT",
|
||||
"port": 2
|
||||
}
|
||||
]
|
||||
}' http://localhost:8080/stats/flowentry/add
|
||||
|
||||
|
||||
@ -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
|
||||
@ -132,7 +132,6 @@ html_static_path = ['_static']
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
# (Deprecated since version 1.6)
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
[parsers]
|
||||
smart_quotes: false
|
||||
@ -16,6 +16,7 @@ Contents:
|
||||
developing.rst
|
||||
configuration.rst
|
||||
tests.rst
|
||||
using_with_openstack.rst
|
||||
snort_integrate.rst
|
||||
app.rst
|
||||
|
||||
|
||||
@ -9,10 +9,7 @@ Ryu provides some useful library for your network applications.
|
||||
|
||||
library_packet.rst
|
||||
library_packet_ref.rst
|
||||
library_pcap.rst
|
||||
library_of_config.rst
|
||||
library_bgp_speaker.rst
|
||||
library_bgp_speaker_ref.rst
|
||||
library_mrt.rst
|
||||
library_ovsdb_manager.rst
|
||||
library_ovsdb.rst
|
||||
|
||||
@ -6,8 +6,8 @@ Introduction
|
||||
============
|
||||
|
||||
Ryu BGP speaker library helps you to enable your code to speak BGP
|
||||
protocol. The library supports IPv4, IPv4 MPLS-labeled VPN, IPv6
|
||||
MPLS-labeled VPN and L2VPN EVPN address families.
|
||||
protocol. The library supports ipv4, ipv4 vpn, and ipv6 vpn address
|
||||
families.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
@ -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
|
||||
*********************
|
||||
|
||||
Path: ``ryu.services.protocols.ovsdb``
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
@ -11,47 +9,17 @@ Ryu OVSDB Manager library allows your code to interact with devices
|
||||
speaking the OVSDB protocol. This enables your code to perform remote
|
||||
management of the devices and react to topology changes on them.
|
||||
|
||||
Please note this library will spawn a server listening on the port 6640 (the
|
||||
IANA registered for OVSDB protocol), but does not initiate connections from
|
||||
controller side.
|
||||
Then, to make your devices connect to Ryu, you need to tell the controller IP
|
||||
address and port to your devices.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Show current configuration
|
||||
$ ovs-vsctl get-manager
|
||||
|
||||
# Set manager (controller) address
|
||||
$ ovs-vsctl set-manager "tcp:127.0.0.1:6640"
|
||||
|
||||
# If you want to specify IPv6 address, wrap ip with brackets
|
||||
$ ovs-vsctl set-manager "tcp:[::1]:6640"
|
||||
|
||||
Also this library identifies the devices by "system-id" which should be unique,
|
||||
persistent identifier among all devices connecting to a single controller.
|
||||
Please make sure "system-id" is configured before connecting.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Show current configuration
|
||||
$ ovs-vsctl get Open_vSwitch . external_ids:system-id
|
||||
|
||||
# Set system-id manually
|
||||
$ ovs-vsctl set Open_vSwitch . external_ids:system-id=<SYSTEM-ID>
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
The following logs all new OVSDB connections in "handle_new_ovsdb_connection"
|
||||
and also provides the API "create_port" for creating a port on a bridge.
|
||||
The following logs all new OVSDB connections and allows creating a port
|
||||
on a bridge.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import uuid
|
||||
|
||||
from ryu.base import app_manager
|
||||
from ryu.controller.handler import set_ev_cls
|
||||
from ryu.services.protocols.ovsdb import api as ovsdb
|
||||
from ryu.services.protocols.ovsdb import event as ovsdb_event
|
||||
|
||||
@ -60,22 +28,16 @@ and also provides the API "create_port" for creating a port on a bridge.
|
||||
@set_ev_cls(ovsdb_event.EventNewOVSDBConnection)
|
||||
def handle_new_ovsdb_connection(self, ev):
|
||||
system_id = ev.system_id
|
||||
address = ev.client.address
|
||||
self.logger.info(
|
||||
'New OVSDB connection from system-id=%s, address=%s',
|
||||
system_id, address)
|
||||
self.logger.info('New OVSDB connection from system id %s',
|
||||
systemd_id)
|
||||
|
||||
# Example: If device has bridge "s1", add port "s1-eth99"
|
||||
if ovsdb.bridge_exists(self, system_id, "s1"):
|
||||
self.create_port(system_id, "s1", "s1-eth99")
|
||||
|
||||
def create_port(self, system_id, bridge_name, name):
|
||||
def create_port(self, systemd_id, bridge_name, name):
|
||||
new_iface_uuid = uuid.uuid4()
|
||||
new_port_uuid = uuid.uuid4()
|
||||
|
||||
bridge = ovsdb.row_by_name(self, system_id, bridge_name)
|
||||
|
||||
def _create_port(tables, insert):
|
||||
bridge = ovsdb.row_by_name(self, system_id, bridge_name)
|
||||
|
||||
iface = insert(tables['Interface'], new_iface_uuid)
|
||||
iface.name = name
|
||||
iface.type = 'internal'
|
||||
@ -84,9 +46,9 @@ and also provides the API "create_port" for creating a port on a bridge.
|
||||
port.name = name
|
||||
port.interfaces = [iface]
|
||||
|
||||
bridge.ports = bridge.ports + [port]
|
||||
brdige.ports = bridfe.ports + [port]
|
||||
|
||||
return new_port_uuid, new_iface_uuid
|
||||
return (new_port_uuid, new_iface_uuid)
|
||||
|
||||
req = ovsdb_event.EventModifyRequest(system_id, _create_port)
|
||||
rep = self.send_request(req)
|
||||
@ -96,4 +58,4 @@ and also provides the API "create_port" for creating a port on a bridge.
|
||||
name, bridge, rep.status)
|
||||
return None
|
||||
|
||||
return rep.insert_uuids[new_port_uuid]
|
||||
return reply.insert_uuid[new_port_uuid]
|
||||
|
||||
@ -14,15 +14,119 @@ Stream Parser class
|
||||
.. automodule:: ryu.lib.packet.stream_parser
|
||||
:members:
|
||||
|
||||
List of the sub-classes:
|
||||
|
||||
- :py:mod:`ryu.lib.packet.bgp.StreamParser`
|
||||
.. autoclass:: ryu.lib.packet.bgp.StreamParser
|
||||
:members:
|
||||
|
||||
Protocol Header classes
|
||||
=======================
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
.. automodule:: ryu.lib.packet.packet_base
|
||||
:members:
|
||||
|
||||
library_packet_ref/packet_base
|
||||
library_packet_ref/*
|
||||
.. automodule:: ryu.lib.packet.ethernet
|
||||
:members:
|
||||
|
||||
.. automodule:: ryu.lib.packet.vlan
|
||||
:members:
|
||||
|
||||
.. automodule:: ryu.lib.packet.pbb
|
||||
:members:
|
||||
|
||||
.. automodule:: ryu.lib.packet.mpls
|
||||
:members:
|
||||
|
||||
.. automodule:: ryu.lib.packet.arp
|
||||
:members:
|
||||
|
||||
.. automodule:: ryu.lib.packet.ipv4
|
||||
:members:
|
||||
|
||||
.. automodule:: ryu.lib.packet.icmp
|
||||
:members:
|
||||
|
||||
.. automodule:: ryu.lib.packet.ipv6
|
||||
:members:
|
||||
|
||||
.. automodule:: ryu.lib.packet.icmpv6
|
||||
:members:
|
||||
|
||||
.. automodule:: ryu.lib.packet.cfm
|
||||
:members:
|
||||
|
||||
.. automodule:: ryu.lib.packet.tcp
|
||||
:members:
|
||||
|
||||
.. automodule:: ryu.lib.packet.udp
|
||||
:members:
|
||||
|
||||
.. autoclass:: ryu.lib.packet.dhcp.dhcp
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.dhcp.options
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.dhcp.option
|
||||
:members:
|
||||
|
||||
.. autoclass:: ryu.lib.packet.vrrp.vrrp
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.vrrp.vrrpv2
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.vrrp.vrrpv3
|
||||
:members:
|
||||
|
||||
.. autoclass:: ryu.lib.packet.slow.slow
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.slow.lacp
|
||||
:members:
|
||||
|
||||
.. autoclass:: ryu.lib.packet.llc.llc
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.llc.ControlFormatI
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.llc.ControlFormatS
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.llc.ControlFormatU
|
||||
:members:
|
||||
|
||||
.. autoclass:: ryu.lib.packet.bpdu.bpdu
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bpdu.ConfigurationBPDUs
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bpdu.TopologyChangeNotificationBPDUs
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bpdu.RstBPDUs
|
||||
:members:
|
||||
|
||||
.. autoclass:: ryu.lib.packet.igmp.igmp
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.igmp.igmpv3_query
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.igmp.igmpv3_report
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.igmp.igmpv3_report_group
|
||||
:members:
|
||||
|
||||
.. autoclass:: ryu.lib.packet.bgp.BGPMessage
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bgp.BGPOpen
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bgp.BGPUpdate
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bgp.BGPKeepAlive
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bgp.BGPNotification
|
||||
:members:
|
||||
.. automodule:: ryu.lib.packet.sctp
|
||||
:members:
|
||||
|
||||
.. autoclass:: ryu.lib.packet.bfd.bfd
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bfd.SimplePassword
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bfd.KeyedMD5
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bfd.MeticulousKeyedMD5
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bfd.KeyedSHA1
|
||||
:members:
|
||||
.. autoclass:: ryu.lib.packet.bfd.MeticulousKeyedSHA1
|
||||
:members:
|
||||
|
||||
@ -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
|
||||
@ -7,63 +7,25 @@ Nicira Extension 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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The followings shows the supported NXAction classes in OF1.3,
|
||||
but also available in OF1.2+.
|
||||
|
||||
.. 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:: NXActionResubmitTable
|
||||
.. 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
|
||||
.. automodule:: ryu.ofproto.nx_match
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ Threads, events, and event queues
|
||||
Ryu applications are single-threaded entities which implement
|
||||
various functionalities in Ryu. Events are messages between them.
|
||||
|
||||
Ryu applications send asynchronous events to each other.
|
||||
Ryu applications send asynchronous events each other.
|
||||
Besides that, there are some Ryu-internal event sources which
|
||||
are not Ryu applications. One of examples of such event sources
|
||||
is OpenFlow controller.
|
||||
@ -22,11 +22,11 @@ between Ryu applications.
|
||||
Each Ryu application has a receive queue for events.
|
||||
The queue is FIFO and preserves the order of events.
|
||||
Each Ryu application has a thread for event processing.
|
||||
The thread keeps draining the receive queue by dequeueing an event
|
||||
The thread keep draining the receive queue by dequeueing an event
|
||||
and calling the appropritate event handler for the event type.
|
||||
Because the event handler is called in the context of
|
||||
the event processing thread, it should be careful when blocking.
|
||||
While an event handler is blocked, no further events for
|
||||
the event processing thread, it should be careful for blocking.
|
||||
I.e. while an event handler is blocked, no further events for
|
||||
the Ryu application will be processed.
|
||||
|
||||
There are kinds of events which are used to implement synchronous
|
||||
@ -82,10 +82,20 @@ For example, EventOFPPacketIn for packet-in message.
|
||||
The OpenFlow controller part of Ryu automatically decodes OpenFlow messages
|
||||
received from switches and send these events to Ryu applications which
|
||||
expressed an interest using ryu.controller.handler.set_ev_cls.
|
||||
OpenFlow event classes are subclasses of the following class.
|
||||
OpenFlow event classes have at least the following attributes.
|
||||
|
||||
.. autoclass:: ryu.controller.ofp_event.EventOFPMsgBase
|
||||
.. tabularcolumns:: |l|L|
|
||||
|
||||
============ =============================================================
|
||||
Attribute Description
|
||||
============ =============================================================
|
||||
msg An object which describes the corresponding OpenFlow message.
|
||||
msg.datapath A ryu.controller.controller.Datapath instance which describes
|
||||
an OpenFlow switch from which we received this OpenFlow message.
|
||||
============ =============================================================
|
||||
|
||||
The msg object has some more additional members whose values are extracted
|
||||
from the original OpenFlow message.
|
||||
See :ref:`ofproto_ref` for more info about OpenFlow messages.
|
||||
|
||||
ryu.base.app_manager.RyuApp
|
||||
@ -93,87 +103,267 @@ ryu.base.app_manager.RyuApp
|
||||
|
||||
See :ref:`api_ref`.
|
||||
|
||||
ryu.controller.handler.set_ev_cls
|
||||
=================================
|
||||
ryu.controller.handler.set_ev_cls(ev_cls, dispatchers=None)
|
||||
===========================================================
|
||||
|
||||
.. autofunction:: ryu.controller.handler.set_ev_cls
|
||||
A decorator for Ryu application to declare an event handler.
|
||||
Decorated method will become an event handler.
|
||||
ev_cls is an event class whose instances this RyuApp wants to receive.
|
||||
dispatchers argument specifies one of the following negotiation phases
|
||||
(or a list of them) for which events should be generated for this handler.
|
||||
Note that, in case an event changes the phase, the phase before the change
|
||||
is used to check the interest.
|
||||
|
||||
.. tabularcolumns:: |l|L|
|
||||
|
||||
=========================================== ==================================
|
||||
Negotiation phase Description
|
||||
=========================================== ==================================
|
||||
ryu.controller.handler.HANDSHAKE_DISPATCHER Sending and waiting for hello
|
||||
message
|
||||
ryu.controller.handler.CONFIG_DISPATCHER Version negotiated and sent
|
||||
features-request message
|
||||
ryu.controller.handler.MAIN_DISPATCHER Switch-features message received
|
||||
and sent set-config message
|
||||
ryu.controller.handler.DEAD_DISPATCHER Disconnect from the peer. Or
|
||||
disconnecting due to some
|
||||
unrecoverable errors.
|
||||
=========================================== ==================================
|
||||
|
||||
ryu.controller.controller.Datapath
|
||||
==================================
|
||||
|
||||
.. autoclass:: ryu.controller.controller.Datapath
|
||||
A class to describe an OpenFlow switch connected to this controller.
|
||||
An instance has the following attributes.
|
||||
|
||||
.. tabularcolumns:: |l|L|
|
||||
|
||||
====================================== =======================================
|
||||
Attribute Description
|
||||
====================================== =======================================
|
||||
id 64-bit OpenFlow Datapath ID.
|
||||
Only available for
|
||||
ryu.controller.handler.MAIN_DISPATCHER
|
||||
phase.
|
||||
ofproto A module which exports OpenFlow
|
||||
definitions, mainly constants appeared
|
||||
in the specification, for the
|
||||
negotiated OpenFlow version. For
|
||||
example, ryu.ofproto.ofproto_v1_0 for
|
||||
OpenFlow 1.0.
|
||||
ofproto_parser A module which exports OpenFlow wire
|
||||
message encoder and decoder for the
|
||||
negotiated OpenFlow version. For
|
||||
example, ryu.ofproto.ofproto_v1_0_parser
|
||||
for OpenFlow 1.0.
|
||||
ofproto_parser.OFPxxxx(datapath, ....) A callable to prepare an OpenFlow
|
||||
message for the given switch. It can
|
||||
be sent with Datapath.send_msg later.
|
||||
xxxx is a name of the message. For
|
||||
example OFPFlowMod for flow-mod
|
||||
message. Arguemnts depend on the
|
||||
message.
|
||||
set_xid(self, msg) Generate an OpenFlow XID and put it
|
||||
in msg.xid.
|
||||
send_msg(self, msg) Queue an OpenFlow message to send to
|
||||
the corresponding switch. If msg.xid
|
||||
is None, set_xid is automatically
|
||||
called on the message before queueing.
|
||||
send_packet_out deprecated
|
||||
send_flow_mod deprecated
|
||||
send_flow_del deprecated
|
||||
send_delete_all_flows deprecated
|
||||
send_barrier Queue an OpenFlow barrier message to
|
||||
send to the switch.
|
||||
send_nxt_set_flow_format deprecated
|
||||
is_reserved_port deprecated
|
||||
====================================== =======================================
|
||||
|
||||
ryu.controller.event.EventBase
|
||||
==============================
|
||||
|
||||
.. autoclass:: ryu.controller.event.EventBase
|
||||
The base of all event classes.
|
||||
A Ryu application can define its own event type by creating a subclass.
|
||||
|
||||
ryu.controller.event.EventRequestBase
|
||||
=====================================
|
||||
|
||||
.. autoclass:: ryu.controller.event.EventRequestBase
|
||||
The base class for synchronous request for RyuApp.send_request.
|
||||
|
||||
ryu.controller.event.EventReplyBase
|
||||
===================================
|
||||
|
||||
.. autoclass:: ryu.controller.event.EventReplyBase
|
||||
The base class for synchronous request reply for RyuApp.send_reply.
|
||||
|
||||
ryu.controller.ofp_event.EventOFPStateChange
|
||||
============================================
|
||||
|
||||
.. autoclass:: ryu.controller.ofp_event.EventOFPStateChange
|
||||
An event class for negotiation phase change notification.
|
||||
An instance of this class is sent to observer after changing
|
||||
the negotiation phase.
|
||||
An instance has at least the following attributes.
|
||||
|
||||
ryu.controller.ofp_event.EventOFPPortStateChange
|
||||
================================================
|
||||
|
||||
.. autoclass:: ryu.controller.ofp_event.EventOFPPortStateChange
|
||||
========= ====================================================================
|
||||
Attribute Description
|
||||
========= ====================================================================
|
||||
datapath ryu.controller.controller.Datapath instance of the switch
|
||||
========= ====================================================================
|
||||
|
||||
ryu.controller.dpset.EventDP
|
||||
============================
|
||||
|
||||
.. autoclass:: ryu.controller.dpset.EventDP
|
||||
An event class to notify connect/disconnect of a switch.
|
||||
For OpenFlow switches, one can get the same notification by observing
|
||||
ryu.controller.ofp_event.EventOFPStateChange.
|
||||
An instance has at least the following attributes.
|
||||
|
||||
========= ====================================================================
|
||||
Attribute Description
|
||||
========= ====================================================================
|
||||
dp A ryu.controller.controller.Datapath instance of the switch
|
||||
enter True when the switch connected to our controller. False for
|
||||
disconnect.
|
||||
========= ====================================================================
|
||||
|
||||
ryu.controller.dpset.EventPortAdd
|
||||
=================================
|
||||
|
||||
.. autoclass:: ryu.controller.dpset.EventPortAdd
|
||||
An event class for switch port status notification.
|
||||
This event is generated when a new port is added to a switch.
|
||||
For OpenFlow switches, one can get the same notification by observing
|
||||
ryu.controller.ofp_event.EventOFPPortStatus.
|
||||
An instance has at least the following attributes.
|
||||
|
||||
========= ====================================================================
|
||||
Attribute Description
|
||||
========= ====================================================================
|
||||
dp A ryu.controller.controller.Datapath instance of the switch
|
||||
port port number
|
||||
========= ====================================================================
|
||||
|
||||
ryu.controller.dpset.EventPortDelete
|
||||
====================================
|
||||
|
||||
.. autoclass:: ryu.controller.dpset.EventPortDelete
|
||||
An event class for switch port status notification.
|
||||
This event is generated when a port is removed from a switch.
|
||||
For OpenFlow switches, one can get the same notification by observing
|
||||
ryu.controller.ofp_event.EventOFPPortStatus.
|
||||
An instance has at least the following attributes.
|
||||
|
||||
========= ====================================================================
|
||||
Attribute Description
|
||||
========= ====================================================================
|
||||
dp A ryu.controller.controller.Datapath instance of the switch
|
||||
port port number
|
||||
========= ====================================================================
|
||||
|
||||
ryu.controller.dpset.EventPortModify
|
||||
====================================
|
||||
|
||||
.. autoclass:: ryu.controller.dpset.EventPortModify
|
||||
An event class for switch port status notification.
|
||||
This event is generated when some attribute of a port is changed.
|
||||
For OpenFlow switches, one can get the same notification by observing
|
||||
ryu.controller.ofp_event.EventOFPPortStatus.
|
||||
An instance has at least the following attributes.
|
||||
|
||||
========= ====================================================================
|
||||
Attribute Description
|
||||
========= ====================================================================
|
||||
dp A ryu.controller.controller.Datapath instance of the switch
|
||||
port port number
|
||||
========= ====================================================================
|
||||
|
||||
ryu.controller.network.EventNetworkPort
|
||||
=======================================
|
||||
|
||||
.. autoclass:: ryu.controller.network.EventNetworkPort
|
||||
An event class for notification of port arrival and deperture.
|
||||
This event is generated when a port is introduced to or removed from a network
|
||||
by the REST API.
|
||||
An instance has at least the following attributes.
|
||||
|
||||
========== ===================================================================
|
||||
Attribute Description
|
||||
========== ===================================================================
|
||||
network_id Network ID
|
||||
dpid OpenFlow Datapath ID of the switch to which the port belongs.
|
||||
port_no OpenFlow port number of the port
|
||||
add_del True for adding a port. False for removing a port.
|
||||
========== ===================================================================
|
||||
|
||||
ryu.controller.network.EventNetworkDel
|
||||
======================================
|
||||
|
||||
.. autoclass:: ryu.controller.network.EventNetworkDel
|
||||
An event class for network deletion.
|
||||
This event is generated when a network is deleted by the REST API.
|
||||
An instance has at least the following attributes.
|
||||
|
||||
========== ===================================================================
|
||||
Attribute Description
|
||||
========== ===================================================================
|
||||
network_id Network ID
|
||||
========== ===================================================================
|
||||
|
||||
ryu.controller.network.EventMacAddress
|
||||
======================================
|
||||
|
||||
.. autoclass:: ryu.controller.network.EventMacAddress
|
||||
An event class for end-point MAC address registration.
|
||||
This event is generated when a end-point MAC address is updated
|
||||
by the REST API.
|
||||
An instance has at least the following attributes.
|
||||
|
||||
=========== ==================================================================
|
||||
Attribute Description
|
||||
=========== ==================================================================
|
||||
network_id Network ID
|
||||
dpid OpenFlow Datapath ID of the switch to which the port belongs.
|
||||
port_no OpenFlow port number of the port
|
||||
mac_address The old MAC address of the port if add_del is False. Otherwise
|
||||
the new MAC address.
|
||||
add_del False if this event is a result of a port removal. Otherwise
|
||||
True.
|
||||
=========== ==================================================================
|
||||
|
||||
ryu.controller.tunnels.EventTunnelKeyAdd
|
||||
========================================
|
||||
|
||||
.. autoclass:: ryu.controller.tunnels.EventTunnelKeyAdd
|
||||
An event class for tunnel key registration.
|
||||
This event is generated when a tunnel key is registered or updated
|
||||
by the REST API.
|
||||
An instance has at least the following attributes.
|
||||
|
||||
=========== ==================================================================
|
||||
Attribute Description
|
||||
=========== ==================================================================
|
||||
network_id Network ID
|
||||
tunnel_key Tunnel Key
|
||||
=========== ==================================================================
|
||||
|
||||
ryu.controller.tunnels.EventTunnelKeyDel
|
||||
========================================
|
||||
|
||||
.. autoclass:: ryu.controller.tunnels.EventTunnelKeyDel
|
||||
An event class for tunnel key registration.
|
||||
This event is generated when a tunnel key is removed by the REST API.
|
||||
An instance has at least the following attributes.
|
||||
|
||||
=========== ==================================================================
|
||||
Attribute Description
|
||||
=========== ==================================================================
|
||||
network_id Network ID
|
||||
tunnel_key Tunnel Key
|
||||
=========== ==================================================================
|
||||
|
||||
ryu.controller.tunnels.EventTunnelPort
|
||||
======================================
|
||||
|
||||
.. autoclass:: ryu.controller.tunnels.EventTunnelPort
|
||||
An event class for tunnel port registration.
|
||||
This event is generated when a tunnel port is added or removed by the REST API.
|
||||
An instance has at least the following attributes.
|
||||
|
||||
=========== ==================================================================
|
||||
Attribute Description
|
||||
=========== ==================================================================
|
||||
dpid OpenFlow Datapath ID
|
||||
port_no OpenFlow port number
|
||||
remote_dpid OpenFlow port number of the tunnel peer
|
||||
add_del True for adding a tunnel. False for removal.
|
||||
=========== ==================================================================
|
||||
|
||||
@ -19,7 +19,7 @@ The test procedure
|
||||
* run LINC switch
|
||||
* run Ryu test_of_config app
|
||||
|
||||
For getting/installing Ryu itself, please refer to https://ryu-sdn.org/
|
||||
For getting/installing Ryu itself, please refer to http://osrg.github.io/ryu/
|
||||
|
||||
|
||||
Install Erlang environment
|
||||
|
||||
@ -4,17 +4,10 @@
|
||||
Using Ryu Network Operating System with OpenStack as OpenFlow controller
|
||||
************************************************************************
|
||||
|
||||
.. CAUTION::
|
||||
|
||||
The Ryu plugin and OFAgent described in the following is deprecated,
|
||||
because Ryu is officially integrated into Open vSwitch agent with
|
||||
"of_interface = native" mode.
|
||||
|
||||
|
||||
Ryu cooperates with OpenStack using Quantum Ryu plugin. The plugin is
|
||||
available in the official Quantum releases.
|
||||
|
||||
For more information, please visit https://github.com/faucetsdn/ryu/wiki/OpenStack .
|
||||
For more information, please visit http://github.com/osrg/ryu/wiki/OpenStack .
|
||||
We described instructions of the installation / configuration of OpenStack
|
||||
with Ryu, and we provide pre-configured VM image to be able to easily try
|
||||
OpenStack with Ryu.
|
||||
|
||||
@ -5,21 +5,21 @@ The First Application
|
||||
Whetting Your Appetite
|
||||
======================
|
||||
|
||||
If you want to manage network gear (switches, routers, etc) your
|
||||
own way, you just need to write your own Ryu application. Your application
|
||||
tells Ryu how you want to manage the gear. Then Ryu configures the
|
||||
gear by using OpenFlow protocol, etc.
|
||||
If you want to manage the network gears (switches, routers, etc) at
|
||||
your way, you need to write your Ryu application. Your application
|
||||
tells Ryu how you want to manage the gears. Then Ryu configures the
|
||||
gears by using OpenFlow protocol, etc.
|
||||
|
||||
Writing Ryu applications is easy. They're just Python scripts.
|
||||
Writing Ryu application is easy. It's just Python scripts.
|
||||
|
||||
|
||||
Start Writing
|
||||
=============
|
||||
|
||||
Here we show a Ryu application that makes an OpenFlow switch work as a dumb
|
||||
We show a Ryu application that make OpenFlow switches work as a dumb
|
||||
layer 2 switch.
|
||||
|
||||
Open a text editor and create a new file with the following content:
|
||||
Open a text editor creating a new file with the following content:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -29,9 +29,9 @@ Open a text editor and create a new file with the following content:
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(L2Switch, self).__init__(*args, **kwargs)
|
||||
|
||||
Ryu applications are just Python scripts so you can save the file with
|
||||
any name, any extension, and any place you want. Let's name the file
|
||||
'l2.py' in your home directory.
|
||||
Ryu application is just a Python script so you can save the file with
|
||||
any name, extensions, and any place you want. Let's name the file
|
||||
'l2.py' at your home directory.
|
||||
|
||||
This application does nothing useful yet, however it's a complete Ryu
|
||||
application. In fact, you can run this Ryu application::
|
||||
@ -41,10 +41,10 @@ application. In fact, you can run this Ryu application::
|
||||
instantiating app /Users/fujita/l2.py
|
||||
|
||||
|
||||
All you have to do is define a new subclass of RyuApp to run
|
||||
All you have to do is defining needs a new subclass of RyuApp to run
|
||||
your Python script as a Ryu application.
|
||||
|
||||
Next let's add some functionality that sends a received packet to all
|
||||
Next let's add the functionality of sending a received packet to all
|
||||
the ports.
|
||||
|
||||
.. code-block:: python
|
||||
@ -53,11 +53,8 @@ the ports.
|
||||
from ryu.controller import ofp_event
|
||||
from ryu.controller.handler import MAIN_DISPATCHER
|
||||
from ryu.controller.handler import set_ev_cls
|
||||
from ryu.ofproto import ofproto_v1_0
|
||||
|
||||
class L2Switch(app_manager.RyuApp):
|
||||
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(L2Switch, self).__init__(*args, **kwargs)
|
||||
|
||||
@ -67,31 +64,26 @@ the ports.
|
||||
dp = msg.datapath
|
||||
ofp = dp.ofproto
|
||||
ofp_parser = dp.ofproto_parser
|
||||
|
||||
|
||||
actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
|
||||
|
||||
data = None
|
||||
if msg.buffer_id == ofp.OFP_NO_BUFFER:
|
||||
data = msg.data
|
||||
|
||||
out = ofp_parser.OFPPacketOut(
|
||||
datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port,
|
||||
actions=actions, data = data)
|
||||
actions=actions)
|
||||
dp.send_msg(out)
|
||||
|
||||
|
||||
A new method 'packet_in_handler' is added to the L2Switch class. This is
|
||||
called when Ryu receives an OpenFlow packet_in message. The trick is the
|
||||
A new method 'packet_in_handler' is added to L2Switch class. This is
|
||||
called when Ryu receives an OpenFlow packet_in message. The trick is
|
||||
'set_ev_cls' decorator. This decorator tells Ryu when the decorated
|
||||
function should be called.
|
||||
|
||||
The first argument of the decorator indicates which type of event this
|
||||
function should be called for. As you might expect, every time Ryu gets a
|
||||
The first argument of the decorator indicates an event that makes
|
||||
function called. As you expect easily, every time Ryu gets a
|
||||
packet_in message, this function is called.
|
||||
|
||||
The second argument indicates the state of the switch. You probably
|
||||
The second argument indicates the state of the switch. Probably, you
|
||||
want to ignore packet_in messages before the negotiation between Ryu
|
||||
and the switch is finished. Using 'MAIN_DISPATCHER' as the second
|
||||
and the switch finishes. Using 'MAIN_DISPATCHER' as the second
|
||||
argument means this function is called only after the negotiation
|
||||
completes.
|
||||
|
||||
@ -108,24 +100,24 @@ Ready for the second half.
|
||||
|
||||
* OFPActionOutput class is used with a packet_out message to specify a
|
||||
switch port that you want to send the packet out of. This
|
||||
application uses the OFPP_FLOOD flag to indicate that the packet should
|
||||
be sent out on all ports.
|
||||
application need a switch to send out of all the ports so OFPP_FLOOD
|
||||
constant is used.
|
||||
|
||||
* OFPPacketOut class is used to build a packet_out message.
|
||||
|
||||
* If you call Datapath class's send_msg method with a OpenFlow message
|
||||
class object, Ryu builds and sends the on-wire data format to the switch.
|
||||
class object, Ryu builds and send the on-wire data format to the switch.
|
||||
|
||||
|
||||
There, you finished implementing your first Ryu application. You are ready to
|
||||
run a Ryu application that does something useful.
|
||||
Here, you finished implementing your first Ryu application. You are ready to
|
||||
run this Ryu application that does something useful.
|
||||
|
||||
|
||||
Is a dumb L2 switch is too dumb? You want to implement a learning L2
|
||||
A dumb l2 switch is too dumb? You want to implement a learning l2
|
||||
switch? Move to `the next step
|
||||
<https://github.com/faucetsdn/ryu/blob/master/ryu/app/simple_switch.py>`_. You
|
||||
<https://github.com/osrg/ryu/blob/master/ryu/app/simple_switch.py>`_. You
|
||||
can learn from the existing Ryu applications at `ryu/app
|
||||
<https://github.com/faucetsdn/ryu/blob/master/ryu/app/>`_ directory and
|
||||
<https://github.com/osrg/ryu/blob/master/ryu/app/>`_ directory and
|
||||
`integrated tests
|
||||
<https://github.com/faucetsdn/ryu/blob/master/ryu/tests/integrated/>`_
|
||||
<https://github.com/osrg/ryu/blob/master/ryu/tests/integrated/>`_
|
||||
directory.
|
||||
|
||||
@ -1 +0,0 @@
|
||||
pip==20.3.4
|
||||
56
run_tests.sh
56
run_tests.sh
@ -8,16 +8,16 @@ usage() {
|
||||
echo "Usage: $0 [OPTION]..."
|
||||
echo "Run Ryu's test suite(s)"
|
||||
echo ""
|
||||
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
|
||||
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
|
||||
echo " -c, --coverage Generate coverage report"
|
||||
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
|
||||
echo " -p, --pycodestyle, --pep8 Just run pycodestyle(pep8)"
|
||||
echo " -P, --no-pycodestyle, --no-pep8 Don't run pycodestyle(pep8)"
|
||||
echo " -l, --pylint Just run pylint"
|
||||
echo " -i, --integrated Run integrated test"
|
||||
echo " -v, --verbose Run verbose pylint analysis"
|
||||
echo " -h, --help Print this usage message"
|
||||
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
|
||||
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
|
||||
echo " -c, --coverage Generate coverage report"
|
||||
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
|
||||
echo " -p, --pep8 Just run pep8"
|
||||
echo " -P, --no-pep8 Don't run pep8"
|
||||
echo " -l, --pylint Just run pylint"
|
||||
echo " -i, --integrated Run integrated test"
|
||||
echo " -v, --verbose Run verbose pylint analysis"
|
||||
echo " -h, --help Print this usage message"
|
||||
echo ""
|
||||
echo "Note: with no options specified, the script will try to run the tests in a virtual environment,"
|
||||
echo " If no virtualenv is found, the script will ask if you would like to create one. If you "
|
||||
@ -31,8 +31,8 @@ process_option() {
|
||||
-V|--virtual-env) always_venv=1; never_venv=0;;
|
||||
-N|--no-virtual-env) always_venv=0; never_venv=1;;
|
||||
-f|--force) force=1;;
|
||||
-p|--pycodestyle|--pep8) just_pycodestyle=1; never_venv=1; always_venv=0;;
|
||||
-P|--no-pycodestyle|--no-pep8) no_pycodestyle=1;;
|
||||
-p|--pep8) just_pep8=1; never_venv=1; always_venv=0;;
|
||||
-P|--no-pep8) no_pep8=1;;
|
||||
-l|--pylint) just_pylint=1;;
|
||||
-i|--integrated) integrated=1;;
|
||||
-c|--coverage) coverage=1;;
|
||||
@ -46,8 +46,8 @@ venv=.venv
|
||||
with_venv=tools/with_venv.sh
|
||||
always_venv=0
|
||||
never_venv=0
|
||||
just_pycodestyle=0
|
||||
no_pycodestyle=0
|
||||
just_pep8=0
|
||||
no_pep8=0
|
||||
just_pylint=0
|
||||
integrated=0
|
||||
force=0
|
||||
@ -103,26 +103,20 @@ run_pylint() {
|
||||
export PYTHONPATH=$OLD_PYTHONPATH
|
||||
}
|
||||
|
||||
run_pycodestyle() {
|
||||
PYCODESTYLE=$(which pycodestyle || which pep8)
|
||||
if [ -z "${PYCODESTYLE}" ]
|
||||
then
|
||||
echo "Please install pycodestyle or pep8"
|
||||
return 1
|
||||
fi
|
||||
echo "Running $(basename ${PYCODESTYLE}) ..."
|
||||
run_pep8() {
|
||||
echo "Running pep8 ..."
|
||||
|
||||
PYCODESTYLE_OPTIONS="--repeat --show-source"
|
||||
PYCODESTYLE_INCLUDE="ryu setup*.py"
|
||||
PYCODESTYLE_LOG=pycodestyle.log
|
||||
${wrapper} ${PYCODESTYLE} $PYCODESTYLE_OPTIONS $PYCODESTYLE_INCLUDE | tee $PYCODESTYLE_LOG
|
||||
PEP8_OPTIONS="--repeat --show-source"
|
||||
PEP8_INCLUDE="ryu setup*.py"
|
||||
PEP8_LOG=pep8.log
|
||||
${wrapper} pep8 $PEP8_OPTIONS $PEP8_INCLUDE | tee $PEP8_LOG
|
||||
}
|
||||
|
||||
run_integrated() {
|
||||
echo "Running integrated test ..."
|
||||
|
||||
INTEGRATED_TEST_RUNNER="./ryu/tests/integrated/run_tests_with_ovs12.py"
|
||||
sudo PYTHONPATH=. nosetests -s $INTEGRATED_TEST_RUNNER
|
||||
sudo PYTHONPATH=. nosetests -s $INTEGRATED_TEST_RUNNER
|
||||
}
|
||||
#NOSETESTS="nosetests $noseopts $noseargs"
|
||||
NOSETESTS="${PYTHON} ./ryu/tests/run_tests.py $noseopts $noseargs"
|
||||
@ -167,8 +161,8 @@ if [ $coverage -eq 1 ]; then
|
||||
${wrapper} coverage erase
|
||||
fi
|
||||
|
||||
if [ $just_pycodestyle -eq 1 ]; then
|
||||
run_pycodestyle
|
||||
if [ $just_pep8 -eq 1 ]; then
|
||||
run_pep8
|
||||
exit
|
||||
fi
|
||||
if [ $just_pylint -eq 1 ]; then
|
||||
@ -183,8 +177,8 @@ fi
|
||||
|
||||
run_tests
|
||||
RV=$?
|
||||
if [ $no_pycodestyle -eq 0 ]; then
|
||||
run_pycodestyle
|
||||
if [ $no_pep8 -eq 0 ]; then
|
||||
run_pep8
|
||||
fi
|
||||
|
||||
if [ $coverage -eq 1 ]; then
|
||||
|
||||
@ -14,5 +14,5 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
version_info = (4, 34)
|
||||
version_info = (4, 1)
|
||||
version = '.'.join(map(str, version_info))
|
||||
|
||||
@ -56,7 +56,7 @@ class GUIServerController(ControllerBase):
|
||||
path = "%s/html/" % PATH
|
||||
self.static_app = DirectoryApp(path)
|
||||
|
||||
@route('topology', '/{filename:[^/]*}')
|
||||
@route('topology', '/{filename:.*}')
|
||||
def static_handler(self, req, **kwargs):
|
||||
if kwargs['filename']:
|
||||
req.path_info = kwargs['filename']
|
||||
|
||||
@ -16,37 +16,22 @@
|
||||
|
||||
# client for ryu.app.ofctl.service
|
||||
|
||||
import numbers
|
||||
|
||||
from ryu.base import app_manager
|
||||
from . import event
|
||||
|
||||
|
||||
def get_datapath(app, dpid=None):
|
||||
def get_datapath(app, dpid):
|
||||
"""
|
||||
Get datapath object by dpid.
|
||||
|
||||
:param app: Client RyuApp instance
|
||||
:param dpid: Datapath ID (int type) or None to get all datapath objects
|
||||
:param dpid: Datapath-id (in integer)
|
||||
|
||||
Returns a object of datapath, a list of datapath objects when no dpid
|
||||
given or None when error.
|
||||
|
||||
Raises an exception if any of the given values is invalid.
|
||||
|
||||
Example::
|
||||
|
||||
# ...(snip)...
|
||||
import ryu.app.ofctl.api as ofctl_api
|
||||
|
||||
|
||||
class MyApp(app_manager.RyuApp):
|
||||
|
||||
def _my_handler(self, ev):
|
||||
# Get all datapath objects
|
||||
result = ofctl_api.get_datapath(self)
|
||||
|
||||
# Get the datapath object which has the given dpid
|
||||
result = ofctl_api.get_datapath(self, dpid=1)
|
||||
Returns None on error.
|
||||
"""
|
||||
assert isinstance(dpid, numbers.Integral)
|
||||
return app.send_request(event.GetDatapathRequest(dpid=dpid))()
|
||||
|
||||
|
||||
@ -70,19 +55,12 @@ def send_msg(app, msg, reply_cls=None, reply_multi=False):
|
||||
|
||||
Example::
|
||||
|
||||
# ...(snip)...
|
||||
import ryu.app.ofctl.api as ofctl_api
|
||||
import ryu.app.ofctl.api as api
|
||||
|
||||
|
||||
class MyApp(app_manager.RyuApp):
|
||||
|
||||
def _my_handler(self, ev):
|
||||
# ...(snip)...
|
||||
msg = parser.OFPPortDescStatsRequest(datapath=datapath)
|
||||
result = ofctl_api.send_msg(
|
||||
self, msg,
|
||||
reply_cls=parser.OFPPortDescStatsReply,
|
||||
reply_multi=True)
|
||||
msg = parser.OFPPortDescStatsRequest(datapath=datapath)
|
||||
result = api.send_msg(self, msg,
|
||||
reply_cls=parser.OFPPortDescStatsReply,
|
||||
reply_multi=True)
|
||||
"""
|
||||
return app.send_request(event.SendMsgRequest(msg=msg,
|
||||
reply_cls=reply_cls,
|
||||
|
||||
@ -33,8 +33,8 @@ class _ReplyBase(event.EventReplyBase):
|
||||
# get datapath
|
||||
|
||||
class GetDatapathRequest(_RequestBase):
|
||||
def __init__(self, dpid=None):
|
||||
assert dpid is None or isinstance(dpid, numbers.Integral)
|
||||
def __init__(self, dpid):
|
||||
assert isinstance(dpid, numbers.Integral)
|
||||
super(GetDatapathRequest, self).__init__()
|
||||
self.dpid = dpid
|
||||
|
||||
|
||||
@ -64,22 +64,6 @@ class OfctlService(app_manager.RyuApp):
|
||||
self.unobserve_event(ev_cls)
|
||||
self.logger.debug('ofctl: stop observing %s', ev_cls)
|
||||
|
||||
def _cancel(self, info, barrier_xid, exception):
|
||||
xid = info.barriers.pop(barrier_xid)
|
||||
req = info.xids.pop(xid)
|
||||
msg = req.msg
|
||||
datapath = msg.datapath
|
||||
parser = datapath.ofproto_parser
|
||||
is_barrier = isinstance(msg, parser.OFPBarrierRequest)
|
||||
|
||||
info.results.pop(xid)
|
||||
|
||||
if not is_barrier and req.reply_cls is not None:
|
||||
self._unobserve_msg(req.reply_cls)
|
||||
|
||||
self.logger.error('failed to send message <%s>', req.msg)
|
||||
self.reply_to_request(req, event.Reply(exception=exception))
|
||||
|
||||
@staticmethod
|
||||
def _is_error(msg):
|
||||
return (ofp_event.ofp_msg_to_ev_cls(type(msg)) ==
|
||||
@ -95,11 +79,6 @@ class OfctlService(app_manager.RyuApp):
|
||||
self.logger.debug('add dpid %s datapath %s new_info %s old_info %s',
|
||||
id, datapath, new_info, old_info)
|
||||
self._switches[id] = new_info
|
||||
if old_info:
|
||||
old_info.datapath.close()
|
||||
for xid in list(old_info.barriers):
|
||||
self._cancel(
|
||||
old_info, xid, exception.InvalidDatapath(result=id))
|
||||
|
||||
@set_ev_cls(ofp_event.EventOFPStateChange, DEAD_DISPATCHER)
|
||||
def _handle_dead(self, ev):
|
||||
@ -115,25 +94,23 @@ class OfctlService(app_manager.RyuApp):
|
||||
if info.datapath is datapath:
|
||||
self.logger.debug('forget info %s', info)
|
||||
self._switches.pop(id)
|
||||
for xid in list(info.barriers):
|
||||
self._cancel(info, xid, exception.InvalidDatapath(result=id))
|
||||
|
||||
@set_ev_cls(event.GetDatapathRequest, MAIN_DISPATCHER)
|
||||
def _handle_get_datapath(self, req):
|
||||
result = None
|
||||
if req.dpid is None:
|
||||
result = [v.datapath for v in self._switches.values()]
|
||||
else:
|
||||
if req.dpid in self._switches:
|
||||
result = self._switches[req.dpid].datapath
|
||||
self.reply_to_request(req, event.Reply(result=result))
|
||||
id = req.dpid
|
||||
assert isinstance(id, numbers.Integral)
|
||||
try:
|
||||
datapath = self._switches[id].datapath
|
||||
except KeyError:
|
||||
datapath = None
|
||||
self.logger.debug('dpid %s -> datapath %s', id, datapath)
|
||||
rep = event.Reply(result=datapath)
|
||||
self.reply_to_request(req, rep)
|
||||
|
||||
@set_ev_cls(event.SendMsgRequest, MAIN_DISPATCHER)
|
||||
def _handle_send_msg(self, req):
|
||||
msg = req.msg
|
||||
datapath = msg.datapath
|
||||
parser = datapath.ofproto_parser
|
||||
is_barrier = isinstance(msg, parser.OFPBarrierRequest)
|
||||
|
||||
try:
|
||||
si = self._switches[datapath.id]
|
||||
@ -144,40 +121,28 @@ class OfctlService(app_manager.RyuApp):
|
||||
self.reply_to_request(req, rep)
|
||||
return
|
||||
|
||||
def _store_xid(xid, barrier_xid):
|
||||
assert xid not in si.results
|
||||
assert xid not in si.xids
|
||||
assert barrier_xid not in si.barriers
|
||||
si.results[xid] = []
|
||||
si.xids[xid] = req
|
||||
si.barriers[barrier_xid] = xid
|
||||
if req.reply_cls is not None:
|
||||
self._observe_msg(req.reply_cls)
|
||||
|
||||
if is_barrier:
|
||||
barrier = msg
|
||||
datapath.set_xid(barrier)
|
||||
_store_xid(barrier.xid, barrier.xid)
|
||||
else:
|
||||
if req.reply_cls is not None:
|
||||
self._observe_msg(req.reply_cls)
|
||||
datapath.set_xid(msg)
|
||||
barrier = datapath.ofproto_parser.OFPBarrierRequest(datapath)
|
||||
datapath.set_xid(barrier)
|
||||
_store_xid(msg.xid, barrier.xid)
|
||||
if not datapath.send_msg(msg):
|
||||
return self._cancel(
|
||||
si, barrier.xid,
|
||||
exception.InvalidDatapath(result=datapath.id))
|
||||
datapath.set_xid(msg)
|
||||
xid = msg.xid
|
||||
barrier = datapath.ofproto_parser.OFPBarrierRequest(datapath)
|
||||
datapath.set_xid(barrier)
|
||||
barrier_xid = barrier.xid
|
||||
assert xid not in si.results
|
||||
assert xid not in si.xids
|
||||
assert barrier_xid not in si.barriers
|
||||
si.results[xid] = []
|
||||
si.xids[xid] = req
|
||||
si.barriers[barrier_xid] = xid
|
||||
|
||||
if not datapath.send_msg(barrier):
|
||||
return self._cancel(
|
||||
si, barrier.xid,
|
||||
exception.InvalidDatapath(result=datapath.id))
|
||||
datapath.send_msg(msg)
|
||||
datapath.send_msg(barrier)
|
||||
|
||||
@set_ev_cls(ofp_event.EventOFPBarrierReply, MAIN_DISPATCHER)
|
||||
def _handle_barrier(self, ev):
|
||||
msg = ev.msg
|
||||
datapath = msg.datapath
|
||||
parser = datapath.ofproto_parser
|
||||
try:
|
||||
si = self._switches[datapath.id]
|
||||
except KeyError:
|
||||
@ -190,12 +155,9 @@ class OfctlService(app_manager.RyuApp):
|
||||
return
|
||||
result = si.results.pop(xid)
|
||||
req = si.xids.pop(xid)
|
||||
is_barrier = isinstance(req.msg, parser.OFPBarrierRequest)
|
||||
if req.reply_cls is not None and not is_barrier:
|
||||
if req.reply_cls is not None:
|
||||
self._unobserve_msg(req.reply_cls)
|
||||
if is_barrier and req.reply_cls == parser.OFPBarrierReply:
|
||||
rep = event.Reply(result=ev.msg)
|
||||
elif any(self._is_error(r) for r in result):
|
||||
if any(self._is_error(r) for r in result):
|
||||
rep = event.Reply(exception=exception.OFError(result=result))
|
||||
elif req.reply_multi:
|
||||
rep = event.Reply(result=result)
|
||||
@ -223,7 +185,7 @@ class OfctlService(app_manager.RyuApp):
|
||||
self.logger.error('unknown error xid %s', msg.xid)
|
||||
return
|
||||
if ((not isinstance(ev, ofp_event.EventOFPErrorMsg)) and
|
||||
(req.reply_cls is None or not isinstance(ev.msg, req.reply_cls))):
|
||||
(req.reply_cls is None or not isinstance(ev.msg, req.reply_cls))):
|
||||
self.logger.error('unexpected reply %s for xid %s', ev, msg.xid)
|
||||
return
|
||||
try:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -21,12 +21,12 @@ This module provides a set of REST API for switch configuration.
|
||||
Used by OpenStack Ryu agent.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from six.moves import http_client
|
||||
import json
|
||||
import logging
|
||||
from webob import Response
|
||||
|
||||
from ryu.app.wsgi import ControllerBase
|
||||
from ryu.app.wsgi import Response
|
||||
from ryu.base import app_manager
|
||||
from ryu.controller import conf_switch
|
||||
from ryu.lib import dpid as dpid_lib
|
||||
@ -111,11 +111,7 @@ class ConfSwitchController(ControllerBase):
|
||||
|
||||
def set_key(self, req, dpid, key, **_kwargs):
|
||||
def _set_val(dpid, key):
|
||||
try:
|
||||
val = req.json if req.body else {}
|
||||
except ValueError:
|
||||
return Response(status=http_client.BAD_REQUEST,
|
||||
body='invalid syntax %s' % req.body)
|
||||
val = json.loads(req.body)
|
||||
self.conf_switch.set_key(dpid, key, val)
|
||||
return None
|
||||
|
||||
|
||||
@ -17,8 +17,9 @@
|
||||
import logging
|
||||
import json
|
||||
|
||||
from webob import Response
|
||||
|
||||
from ryu.app.wsgi import ControllerBase
|
||||
from ryu.app.wsgi import Response
|
||||
from ryu.app.wsgi import WSGIApplication
|
||||
from ryu.base import app_manager
|
||||
from ryu.controller import ofp_event
|
||||
@ -491,8 +492,8 @@ class FirewallController(ControllerBase):
|
||||
|
||||
def _set_rule(self, req, switchid, vlan_id=VLANID_NONE):
|
||||
try:
|
||||
rule = req.json if req.body else {}
|
||||
except ValueError:
|
||||
rule = json.loads(req.body)
|
||||
except SyntaxError:
|
||||
FirewallController._LOGGER.debug('invalid syntax %s', req.body)
|
||||
return Response(status=400)
|
||||
|
||||
@ -515,8 +516,8 @@ class FirewallController(ControllerBase):
|
||||
|
||||
def _delete_rule(self, req, switchid, vlan_id=VLANID_NONE):
|
||||
try:
|
||||
ruleid = req.json if req.body else {}
|
||||
except ValueError:
|
||||
ruleid = json.loads(req.body)
|
||||
except SyntaxError:
|
||||
FirewallController._LOGGER.debug('invalid syntax %s', req.body)
|
||||
return Response(status=400)
|
||||
|
||||
@ -678,7 +679,8 @@ class Firewall(object):
|
||||
|
||||
def _set_log_status(self, is_enable, waiters):
|
||||
if is_enable:
|
||||
actions = Action.to_openflow({REST_ACTION: REST_ACTION_PACKETIN})
|
||||
actions = Action.to_openflow(self.dp,
|
||||
{REST_ACTION: REST_ACTION_PACKETIN})
|
||||
details = 'Log collection started.'
|
||||
else:
|
||||
actions = []
|
||||
@ -720,7 +722,7 @@ class Firewall(object):
|
||||
priority = ARP_FLOW_PRIORITY
|
||||
match = {REST_DL_TYPE: ether.ETH_TYPE_ARP}
|
||||
action = {REST_ACTION: REST_ACTION_ALLOW}
|
||||
actions = Action.to_openflow(action)
|
||||
actions = Action.to_openflow(self.dp, action)
|
||||
flow = self._to_of_flow(cookie=cookie, priority=priority,
|
||||
match=match, actions=actions)
|
||||
|
||||
@ -752,7 +754,7 @@ class Firewall(object):
|
||||
result = self.get_log_status(waiters)
|
||||
if result[REST_LOG_STATUS] == REST_STATUS_ENABLE:
|
||||
rest[REST_ACTION] = REST_ACTION_PACKETIN
|
||||
actions = Action.to_openflow(rest)
|
||||
actions = Action.to_openflow(self.dp, rest)
|
||||
flow = self._to_of_flow(cookie=cookie, priority=priority,
|
||||
match=match, actions=actions)
|
||||
|
||||
@ -879,7 +881,7 @@ class Firewall(object):
|
||||
rule = {REST_RULE_ID: ruleid}
|
||||
rule.update({REST_PRIORITY: flow[REST_PRIORITY]})
|
||||
rule.update(Match.to_rest(flow))
|
||||
rule.update(Action.to_rest(flow))
|
||||
rule.update(Action.to_rest(self.dp, flow))
|
||||
return rule
|
||||
|
||||
|
||||
@ -1077,17 +1079,19 @@ class Match(object):
|
||||
class Action(object):
|
||||
|
||||
@staticmethod
|
||||
def to_openflow(rest):
|
||||
def to_openflow(dp, rest):
|
||||
value = rest.get(REST_ACTION, REST_ACTION_ALLOW)
|
||||
|
||||
if value == REST_ACTION_ALLOW:
|
||||
out_port = dp.ofproto.OFPP_NORMAL
|
||||
action = [{'type': 'OUTPUT',
|
||||
'port': 'NORMAL'}]
|
||||
'port': out_port}]
|
||||
elif value == REST_ACTION_DENY:
|
||||
action = []
|
||||
elif value == REST_ACTION_PACKETIN:
|
||||
out_port = dp.ofproto.OFPP_CONTROLLER
|
||||
action = [{'type': 'OUTPUT',
|
||||
'port': 'CONTROLLER',
|
||||
'port': out_port,
|
||||
'max_len': 128}]
|
||||
else:
|
||||
raise ValueError('Invalid action type.')
|
||||
@ -1095,9 +1099,9 @@ class Action(object):
|
||||
return action
|
||||
|
||||
@staticmethod
|
||||
def to_rest(openflow):
|
||||
def to_rest(dp, openflow):
|
||||
if REST_ACTION in openflow:
|
||||
action_allow = 'OUTPUT:NORMAL'
|
||||
action_allow = 'OUTPUT:%d' % dp.ofproto.OFPP_NORMAL
|
||||
if openflow[REST_ACTION] == [action_allow]:
|
||||
action = {REST_ACTION: REST_ACTION_ALLOW}
|
||||
else:
|
||||
|
||||
@ -18,11 +18,10 @@ import logging
|
||||
import json
|
||||
import re
|
||||
|
||||
from webob import Response
|
||||
|
||||
from ryu.app import conf_switch_key as cs_key
|
||||
from ryu.app.wsgi import ControllerBase
|
||||
from ryu.app.wsgi import Response
|
||||
from ryu.app.wsgi import route
|
||||
from ryu.app.wsgi import WSGIApplication
|
||||
from ryu.app.wsgi import ControllerBase, WSGIApplication, route
|
||||
from ryu.base import app_manager
|
||||
from ryu.controller import conf_switch
|
||||
from ryu.controller import ofp_event
|
||||
@ -425,8 +424,7 @@ class QoSController(ControllerBase):
|
||||
@staticmethod
|
||||
def delete_ovsdb_addr(dpid):
|
||||
ofs = QoSController._OFS_LIST.get(dpid, None)
|
||||
if ofs is not None:
|
||||
ofs.set_ovsdb_addr(dpid, None)
|
||||
ofs.set_ovsdb_addr(dpid, None)
|
||||
|
||||
@route('qos_switch', BASE_URL + '/queue/{switchid}',
|
||||
methods=['GET'], requirements=REQUIREMENTS)
|
||||
@ -508,8 +506,8 @@ class QoSController(ControllerBase):
|
||||
|
||||
def _access_switch(self, req, switchid, vlan_id, func, waiters):
|
||||
try:
|
||||
rest = req.json if req.body else {}
|
||||
except ValueError:
|
||||
rest = json.loads(req.body) if req.body else {}
|
||||
except SyntaxError:
|
||||
QoSController._LOGGER.debug('invalid syntax %s', req.body)
|
||||
return Response(status=400)
|
||||
|
||||
@ -558,22 +556,6 @@ class QoS(object):
|
||||
self.vlan_list[VLANID_NONE] = 0 # for VLAN=None
|
||||
self.dp = dp
|
||||
self.version = dp.ofproto.OFP_VERSION
|
||||
# Dictionary of port name to Queue config.
|
||||
# e.g.)
|
||||
# self.queue_list = {
|
||||
# "s1-eth1": {
|
||||
# "0": {
|
||||
# "config": {
|
||||
# "max-rate": "600000"
|
||||
# }
|
||||
# },
|
||||
# "1": {
|
||||
# "config": {
|
||||
# "min-rate": "900000"
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
self.queue_list = {}
|
||||
self.CONF = CONF
|
||||
self.ovsdb_addr = None
|
||||
@ -601,22 +583,25 @@ class QoS(object):
|
||||
self.ofctl.mod_flow_entry(self.dp, flow, cmd)
|
||||
|
||||
def set_ovsdb_addr(self, dpid, ovsdb_addr):
|
||||
# easy check if the address format valid
|
||||
_proto, _host, _port = ovsdb_addr.split(':')
|
||||
|
||||
old_address = self.ovsdb_addr
|
||||
if old_address == ovsdb_addr:
|
||||
return
|
||||
elif ovsdb_addr is None:
|
||||
# Determine deleting OVSDB address was requested.
|
||||
if ovsdb_addr is None:
|
||||
if self.ovs_bridge:
|
||||
self.ovs_bridge.del_controller()
|
||||
self.ovs_bridge = None
|
||||
return
|
||||
|
||||
ovs_bridge = bridge.OVSBridge(self.CONF, dpid, ovsdb_addr)
|
||||
try:
|
||||
ovs_bridge.init()
|
||||
except:
|
||||
raise ValueError('ovsdb addr is not available.')
|
||||
self.ovsdb_addr = ovsdb_addr
|
||||
self.ovs_bridge = ovs_bridge
|
||||
if self.ovs_bridge is None:
|
||||
ovs_bridge = bridge.OVSBridge(self.CONF, dpid, ovsdb_addr)
|
||||
self.ovs_bridge = ovs_bridge
|
||||
try:
|
||||
ovs_bridge.init()
|
||||
except:
|
||||
raise ValueError('ovsdb addr is not available.')
|
||||
|
||||
def _update_vlan_list(self, vlan_list):
|
||||
for vlan_id in self.vlan_list.keys():
|
||||
@ -679,15 +664,7 @@ class QoS(object):
|
||||
'details': 'ovs_bridge is not exists'}
|
||||
return REST_COMMAND_RESULT, msg
|
||||
|
||||
port_name = rest.get(REST_PORT_NAME, None)
|
||||
vif_ports = self.ovs_bridge.get_port_name_list()
|
||||
|
||||
if port_name is not None:
|
||||
if port_name not in vif_ports:
|
||||
raise ValueError('%s port is not exists' % port_name)
|
||||
vif_ports = [port_name]
|
||||
|
||||
queue_list = {}
|
||||
self.queue_list.clear()
|
||||
queue_type = rest.get(REST_QUEUE_TYPE, 'linux-htb')
|
||||
parent_max_rate = rest.get(REST_QUEUE_MAX_RATE, None)
|
||||
queues = rest.get(REST_QUEUES, [])
|
||||
@ -705,9 +682,17 @@ class QoS(object):
|
||||
config['min-rate'] = min_rate
|
||||
if len(config):
|
||||
queue_config.append(config)
|
||||
queue_list[queue_id] = {'config': config}
|
||||
self.queue_list[queue_id] = {'config': config}
|
||||
queue_id += 1
|
||||
|
||||
port_name = rest.get(REST_PORT_NAME, None)
|
||||
vif_ports = self.ovs_bridge.get_port_name_list()
|
||||
|
||||
if port_name is not None:
|
||||
if port_name not in vif_ports:
|
||||
raise ValueError('%s port is not exists' % port_name)
|
||||
vif_ports = [port_name]
|
||||
|
||||
for port_name in vif_ports:
|
||||
try:
|
||||
self.ovs_bridge.set_qos(port_name, type=queue_type,
|
||||
@ -715,10 +700,9 @@ class QoS(object):
|
||||
queues=queue_config)
|
||||
except Exception as msg:
|
||||
raise ValueError(msg)
|
||||
self.queue_list[port_name] = queue_list
|
||||
|
||||
msg = {'result': 'success',
|
||||
'details': queue_list}
|
||||
'details': self.queue_list}
|
||||
|
||||
return REST_COMMAND_RESULT, msg
|
||||
|
||||
@ -733,9 +717,9 @@ class QoS(object):
|
||||
|
||||
@rest_command
|
||||
def delete_queue(self, rest, vlan_id):
|
||||
self.queue_list.clear()
|
||||
if self._delete_queue():
|
||||
msg = 'success'
|
||||
self.queue_list.clear()
|
||||
else:
|
||||
msg = 'failure'
|
||||
|
||||
@ -1143,20 +1127,20 @@ class Match(object):
|
||||
class Action(object):
|
||||
|
||||
@staticmethod
|
||||
def to_rest(flow):
|
||||
if REST_ACTION in flow:
|
||||
def to_rest(openflow):
|
||||
if REST_ACTION in openflow:
|
||||
actions = []
|
||||
for act in flow[REST_ACTION]:
|
||||
field_value = re.search(r'SET_FIELD: \{ip_dscp:(\d+)', act)
|
||||
for action in openflow[REST_ACTION]:
|
||||
field_value = re.search('SET_FIELD: {ip_dscp:(\d+)', action)
|
||||
if field_value:
|
||||
actions.append({REST_ACTION_MARK: field_value.group(1)})
|
||||
meter_value = re.search(r'METER:(\d+)', act)
|
||||
meter_value = re.search('METER:(\d+)', action)
|
||||
if meter_value:
|
||||
actions.append({REST_ACTION_METER: meter_value.group(1)})
|
||||
queue_value = re.search(r'SET_QUEUE:(\d+)', act)
|
||||
queue_value = re.search('SET_QUEUE:(\d+)', action)
|
||||
if queue_value:
|
||||
actions.append({REST_ACTION_QUEUE: queue_value.group(1)})
|
||||
action = {REST_ACTION: actions}
|
||||
action = {REST_ACTION: actions}
|
||||
else:
|
||||
action = {REST_ACTION: 'Unknown action type.'}
|
||||
|
||||
|
||||
@ -20,9 +20,9 @@ import socket
|
||||
import struct
|
||||
|
||||
import json
|
||||
from webob import Response
|
||||
|
||||
from ryu.app.wsgi import ControllerBase
|
||||
from ryu.app.wsgi import Response
|
||||
from ryu.app.wsgi import WSGIApplication
|
||||
from ryu.base import app_manager
|
||||
from ryu.controller import dpset
|
||||
@ -40,7 +40,6 @@ from ryu.lib.packet import ethernet
|
||||
from ryu.lib.packet import icmp
|
||||
from ryu.lib.packet import ipv4
|
||||
from ryu.lib.packet import packet
|
||||
from ryu.lib.packet import packet_base
|
||||
from ryu.lib.packet import tcp
|
||||
from ryu.lib.packet import udp
|
||||
from ryu.lib.packet import vlan
|
||||
@ -377,45 +376,42 @@ class RouterController(ControllerBase):
|
||||
@rest_command
|
||||
def get_data(self, req, switch_id, **_kwargs):
|
||||
return self._access_router(switch_id, VLANID_NONE,
|
||||
'get_data', req)
|
||||
'get_data', req.body)
|
||||
|
||||
# GET /router/{switch_id}/{vlan_id}
|
||||
@rest_command
|
||||
def get_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
|
||||
return self._access_router(switch_id, vlan_id,
|
||||
'get_data', req)
|
||||
'get_data', req.body)
|
||||
|
||||
# POST /router/{switch_id}
|
||||
@rest_command
|
||||
def set_data(self, req, switch_id, **_kwargs):
|
||||
return self._access_router(switch_id, VLANID_NONE,
|
||||
'set_data', req)
|
||||
'set_data', req.body)
|
||||
|
||||
# POST /router/{switch_id}/{vlan_id}
|
||||
@rest_command
|
||||
def set_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
|
||||
return self._access_router(switch_id, vlan_id,
|
||||
'set_data', req)
|
||||
'set_data', req.body)
|
||||
|
||||
# DELETE /router/{switch_id}
|
||||
@rest_command
|
||||
def delete_data(self, req, switch_id, **_kwargs):
|
||||
return self._access_router(switch_id, VLANID_NONE,
|
||||
'delete_data', req)
|
||||
'delete_data', req.body)
|
||||
|
||||
# DELETE /router/{switch_id}/{vlan_id}
|
||||
@rest_command
|
||||
def delete_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
|
||||
return self._access_router(switch_id, vlan_id,
|
||||
'delete_data', req)
|
||||
'delete_data', req.body)
|
||||
|
||||
def _access_router(self, switch_id, vlan_id, func, req):
|
||||
def _access_router(self, switch_id, vlan_id, func, rest_param):
|
||||
rest_message = []
|
||||
routers = self._get_router(switch_id)
|
||||
try:
|
||||
param = req.json if req.body else {}
|
||||
except ValueError:
|
||||
raise SyntaxError('invalid syntax %s', req.body)
|
||||
param = json.loads(rest_param) if rest_param else {}
|
||||
for router in routers.values():
|
||||
function = getattr(router, func)
|
||||
data = function(vlan_id, param, self.waiters)
|
||||
@ -570,8 +566,7 @@ class Router(dict):
|
||||
# TODO: Packet library convert to string
|
||||
# self.logger.debug('Packet in = %s', str(pkt), self.sw_id)
|
||||
header_list = dict((p.protocol_name, p)
|
||||
for p in pkt.protocols
|
||||
if isinstance(p, packet_base.PacketBase))
|
||||
for p in pkt.protocols if type(p) != str)
|
||||
if header_list:
|
||||
# Check vlan-tag
|
||||
vlan_id = VLANID_NONE
|
||||
@ -1010,14 +1005,14 @@ class VlanRouter(object):
|
||||
else:
|
||||
if header_list[ARP].opcode == arp.ARP_REQUEST:
|
||||
# ARP request to router port -> send ARP reply
|
||||
src_mac = self.port_data[in_port].mac
|
||||
dst_mac = header_list[ARP].src_mac
|
||||
src_mac = header_list[ARP].src_mac
|
||||
dst_mac = self.port_data[in_port].mac
|
||||
arp_target_mac = dst_mac
|
||||
output = in_port
|
||||
in_port = self.ofctl.dp.ofproto.OFPP_CONTROLLER
|
||||
|
||||
self.ofctl.send_arp(arp.ARP_REPLY, self.vlan_id,
|
||||
src_mac, dst_mac, dst_ip, src_ip,
|
||||
dst_mac, src_mac, dst_ip, src_ip,
|
||||
arp_target_mac, in_port, output)
|
||||
|
||||
log_msg = 'Receive ARP request from [%s] to router port [%s].'
|
||||
|
||||
@ -14,11 +14,9 @@
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
from webob import Response
|
||||
|
||||
from ryu.app.wsgi import ControllerBase
|
||||
from ryu.app.wsgi import Response
|
||||
from ryu.app.wsgi import route
|
||||
from ryu.app.wsgi import WSGIApplication
|
||||
from ryu.app.wsgi import ControllerBase, WSGIApplication, route
|
||||
from ryu.base import app_manager
|
||||
from ryu.lib import dpid as dpid_lib
|
||||
from ryu.topology.api import get_switch, get_link, get_host
|
||||
|
||||
1842
ryu/app/rest_vtep.py
1842
ryu/app/rest_vtep.py
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
self.mac_to_port = {}
|
||||
|
||||
def add_flow(self, datapath, in_port, dst, src, actions):
|
||||
def add_flow(self, datapath, in_port, dst, actions):
|
||||
ofproto = datapath.ofproto
|
||||
|
||||
match = datapath.ofproto_parser.OFPMatch(
|
||||
in_port=in_port,
|
||||
dl_dst=haddr_to_bin(dst), dl_src=haddr_to_bin(src))
|
||||
in_port=in_port, dl_dst=haddr_to_bin(dst))
|
||||
|
||||
mod = datapath.ofproto_parser.OFPFlowMod(
|
||||
datapath=datapath, match=match, cookie=0,
|
||||
@ -82,7 +81,7 @@ class SimpleSwitch(app_manager.RyuApp):
|
||||
|
||||
# install a flow to avoid packet_in next time
|
||||
if out_port != ofproto.OFPP_FLOOD:
|
||||
self.add_flow(datapath, msg.in_port, dst, src, actions)
|
||||
self.add_flow(datapath, msg.in_port, dst, actions)
|
||||
|
||||
data = None
|
||||
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
|
||||
|
||||
@ -30,12 +30,11 @@ class SimpleSwitch12(app_manager.RyuApp):
|
||||
super(SimpleSwitch12, self).__init__(*args, **kwargs)
|
||||
self.mac_to_port = {}
|
||||
|
||||
def add_flow(self, datapath, port, dst, src, actions):
|
||||
def add_flow(self, datapath, port, dst, actions):
|
||||
ofproto = datapath.ofproto
|
||||
|
||||
match = datapath.ofproto_parser.OFPMatch(in_port=port,
|
||||
eth_dst=dst,
|
||||
eth_src=src)
|
||||
eth_dst=dst)
|
||||
inst = [datapath.ofproto_parser.OFPInstructionActions(
|
||||
ofproto.OFPIT_APPLY_ACTIONS, actions)]
|
||||
|
||||
@ -81,7 +80,7 @@ class SimpleSwitch12(app_manager.RyuApp):
|
||||
|
||||
# install a flow to avoid packet_in next time
|
||||
if out_port != ofproto.OFPP_FLOOD:
|
||||
self.add_flow(datapath, in_port, dst, src, actions)
|
||||
self.add_flow(datapath, in_port, dst, actions)
|
||||
|
||||
data = None
|
||||
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
|
||||
|
||||
@ -85,7 +85,7 @@ class SimpleSwitch13(app_manager.RyuApp):
|
||||
dst = eth.dst
|
||||
src = eth.src
|
||||
|
||||
dpid = format(datapath.id, "d").zfill(16)
|
||||
dpid = datapath.id
|
||||
self.mac_to_port.setdefault(dpid, {})
|
||||
|
||||
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
|
||||
@ -102,7 +102,7 @@ class SimpleSwitch13(app_manager.RyuApp):
|
||||
|
||||
# install a flow to avoid packet_in next time
|
||||
if out_port != ofproto.OFPP_FLOOD:
|
||||
match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
|
||||
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
|
||||
# verify if we have a valid buffer_id, if yes avoid to send both
|
||||
# flow_mod & packet_out
|
||||
if msg.buffer_id != ofproto.OFP_NO_BUFFER:
|
||||
|
||||
@ -93,7 +93,7 @@ class SimpleSwitch14(app_manager.RyuApp):
|
||||
|
||||
# install a flow to avoid packet_in next time
|
||||
if out_port != ofproto.OFPP_FLOOD:
|
||||
match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
|
||||
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
|
||||
self.add_flow(datapath, 1, match, actions)
|
||||
|
||||
data = None
|
||||
|
||||
@ -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)
|
||||
@ -1,121 +0,0 @@
|
||||
# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from 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 import dpid as dpid_lib
|
||||
from ryu.lib import stplib
|
||||
from ryu.lib.packet import packet
|
||||
from ryu.lib.packet import ethernet
|
||||
from ryu.app import simple_switch_13
|
||||
|
||||
|
||||
class SimpleSwitch13(simple_switch_13.SimpleSwitch13):
|
||||
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
|
||||
_CONTEXTS = {'stplib': stplib.Stp}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SimpleSwitch13, self).__init__(*args, **kwargs)
|
||||
self.mac_to_port = {}
|
||||
self.stp = kwargs['stplib']
|
||||
|
||||
# Sample of stplib config.
|
||||
# please refer to stplib.Stp.set_config() for details.
|
||||
config = {dpid_lib.str_to_dpid('0000000000000001'):
|
||||
{'bridge': {'priority': 0x8000}},
|
||||
dpid_lib.str_to_dpid('0000000000000002'):
|
||||
{'bridge': {'priority': 0x9000}},
|
||||
dpid_lib.str_to_dpid('0000000000000003'):
|
||||
{'bridge': {'priority': 0xa000}}}
|
||||
self.stp.set_config(config)
|
||||
|
||||
def delete_flow(self, datapath):
|
||||
ofproto = datapath.ofproto
|
||||
parser = datapath.ofproto_parser
|
||||
|
||||
for dst in self.mac_to_port[datapath.id].keys():
|
||||
match = parser.OFPMatch(eth_dst=dst)
|
||||
mod = parser.OFPFlowMod(
|
||||
datapath, command=ofproto.OFPFC_DELETE,
|
||||
out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY,
|
||||
priority=1, match=match)
|
||||
datapath.send_msg(mod)
|
||||
|
||||
@set_ev_cls(stplib.EventPacketIn, MAIN_DISPATCHER)
|
||||
def _packet_in_handler(self, ev):
|
||||
msg = ev.msg
|
||||
datapath = msg.datapath
|
||||
ofproto = datapath.ofproto
|
||||
parser = datapath.ofproto_parser
|
||||
in_port = msg.match['in_port']
|
||||
|
||||
pkt = packet.Packet(msg.data)
|
||||
eth = pkt.get_protocols(ethernet.ethernet)[0]
|
||||
|
||||
dst = eth.dst
|
||||
src = eth.src
|
||||
|
||||
dpid = datapath.id
|
||||
self.mac_to_port.setdefault(dpid, {})
|
||||
|
||||
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
|
||||
|
||||
# learn a mac address to avoid FLOOD next time.
|
||||
self.mac_to_port[dpid][src] = in_port
|
||||
|
||||
if dst in self.mac_to_port[dpid]:
|
||||
out_port = self.mac_to_port[dpid][dst]
|
||||
else:
|
||||
out_port = ofproto.OFPP_FLOOD
|
||||
|
||||
actions = [parser.OFPActionOutput(out_port)]
|
||||
|
||||
# install a flow to avoid packet_in next time
|
||||
if out_port != ofproto.OFPP_FLOOD:
|
||||
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
|
||||
self.add_flow(datapath, 1, match, actions)
|
||||
|
||||
data = None
|
||||
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
|
||||
data = msg.data
|
||||
|
||||
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
|
||||
in_port=in_port, actions=actions, data=data)
|
||||
datapath.send_msg(out)
|
||||
|
||||
@set_ev_cls(stplib.EventTopologyChange, MAIN_DISPATCHER)
|
||||
def _topology_change_handler(self, ev):
|
||||
dp = ev.dp
|
||||
dpid_str = dpid_lib.dpid_to_str(dp.id)
|
||||
msg = 'Receive topology change event. Flush MAC table.'
|
||||
self.logger.debug("[dpid=%s] %s", dpid_str, msg)
|
||||
|
||||
if dp.id in self.mac_to_port:
|
||||
self.delete_flow(dp)
|
||||
del self.mac_to_port[dp.id]
|
||||
|
||||
@set_ev_cls(stplib.EventPortStateChange, MAIN_DISPATCHER)
|
||||
def _port_state_change_handler(self, ev):
|
||||
dpid_str = dpid_lib.dpid_to_str(ev.dp.id)
|
||||
of_state = {stplib.PORT_STATE_DISABLE: 'DISABLE',
|
||||
stplib.PORT_STATE_BLOCK: 'BLOCK',
|
||||
stplib.PORT_STATE_LISTEN: 'LISTEN',
|
||||
stplib.PORT_STATE_LEARN: 'LEARN',
|
||||
stplib.PORT_STATE_FORWARD: 'FORWARD'}
|
||||
self.logger.debug("[dpid=%s][port=%d] state=%s",
|
||||
dpid_str, ev.port_no, of_state[ev.port_state])
|
||||
@ -42,14 +42,15 @@ Get arp table:
|
||||
15:0c:de:49": 2}}}
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from webob import Response
|
||||
from ryu.app import simple_switch_13
|
||||
from ryu.app.wsgi import ControllerBase
|
||||
from ryu.app.wsgi import rpc_public
|
||||
from ryu.app.wsgi import websocket
|
||||
from ryu.app.wsgi import WebSocketRPCServer
|
||||
from ryu.app.wsgi import WSGIApplication
|
||||
from ryu.app.wsgi import route, websocket, ControllerBase, WSGIApplication
|
||||
from ryu.app.wsgi import rpc_public, WebSocketRPCServer
|
||||
from ryu.controller import ofp_event
|
||||
from ryu.controller.handler import set_ev_cls
|
||||
from ryu.lib import hub
|
||||
from ryu.lib.packet import packet
|
||||
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ $ sudo mn --controller=remote --topo linear,2
|
||||
""" # noqa
|
||||
|
||||
from socket import error as SocketError
|
||||
from tinyrpc.exc import InvalidReplyError
|
||||
from ryu.contrib.tinyrpc.exc import InvalidReplyError
|
||||
|
||||
|
||||
from ryu.app.wsgi import (
|
||||
|
||||
@ -17,34 +17,27 @@
|
||||
import inspect
|
||||
from types import MethodType
|
||||
|
||||
import webob.dec
|
||||
from webob.response import Response
|
||||
from ryu import cfg
|
||||
from ryu.lib import hub
|
||||
from routes import Mapper
|
||||
from routes.util import URLGenerator
|
||||
import six
|
||||
|
||||
import ryu.contrib
|
||||
ryu.contrib.update_module_path()
|
||||
from tinyrpc.server import RPCServer
|
||||
from tinyrpc.dispatch import RPCDispatcher
|
||||
from tinyrpc.dispatch import public as rpc_public
|
||||
from tinyrpc.protocols.jsonrpc import JSONRPCProtocol
|
||||
from tinyrpc.transports import ServerTransport, ClientTransport
|
||||
from tinyrpc.client import RPCClient
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
from webob.request import Request as webob_Request
|
||||
from webob.response import Response as webob_Response
|
||||
|
||||
from ryu import cfg
|
||||
from ryu.lib import hub
|
||||
|
||||
DEFAULT_WSGI_HOST = '0.0.0.0'
|
||||
DEFAULT_WSGI_PORT = 8080
|
||||
ryu.contrib.restore_module_path()
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_cli_opts([
|
||||
cfg.StrOpt(
|
||||
'wsapi-host', default=DEFAULT_WSGI_HOST,
|
||||
help='webapp listen host (default %s)' % DEFAULT_WSGI_HOST),
|
||||
cfg.IntOpt(
|
||||
'wsapi-port', default=DEFAULT_WSGI_PORT,
|
||||
help='webapp listen port (default %s)' % DEFAULT_WSGI_PORT),
|
||||
cfg.StrOpt('wsapi-host', default='', help='webapp listen host'),
|
||||
cfg.IntOpt('wsapi-port', default=8080, help='webapp listen port')
|
||||
])
|
||||
|
||||
HEX_PATTERN = r'0x[0-9a-z]+'
|
||||
@ -63,33 +56,6 @@ def route(name, path, methods=None, requirements=None):
|
||||
return _route
|
||||
|
||||
|
||||
class Request(webob_Request):
|
||||
"""
|
||||
Wrapper class for webob.request.Request.
|
||||
|
||||
The behavior of this class is the same as webob.request.Request
|
||||
except for setting "charset" to "UTF-8" automatically.
|
||||
"""
|
||||
DEFAULT_CHARSET = "UTF-8"
|
||||
|
||||
def __init__(self, environ, charset=DEFAULT_CHARSET, *args, **kwargs):
|
||||
super(Request, self).__init__(
|
||||
environ, charset=charset, *args, **kwargs)
|
||||
|
||||
|
||||
class Response(webob_Response):
|
||||
"""
|
||||
Wrapper class for webob.response.Response.
|
||||
|
||||
The behavior of this class is the same as webob.response.Response
|
||||
except for setting "charset" to "UTF-8" automatically.
|
||||
"""
|
||||
DEFAULT_CHARSET = "UTF-8"
|
||||
|
||||
def __init__(self, charset=DEFAULT_CHARSET, *args, **kwargs):
|
||||
super(Response, self).__init__(charset=charset, *args, **kwargs)
|
||||
|
||||
|
||||
class WebSocketRegistrationWrapper(object):
|
||||
|
||||
def __init__(self, func, controller):
|
||||
@ -108,15 +74,8 @@ class WebSocketRegistrationWrapper(object):
|
||||
|
||||
class _AlreadyHandledResponse(Response):
|
||||
# XXX: Eventlet API should not be used directly.
|
||||
# https://github.com/benoitc/gunicorn/pull/2581
|
||||
from packaging import version
|
||||
import eventlet
|
||||
if version.parse(eventlet.__version__) >= version.parse("0.30.3"):
|
||||
import eventlet.wsgi
|
||||
_ALREADY_HANDLED = getattr(eventlet.wsgi, "ALREADY_HANDLED", None)
|
||||
else:
|
||||
from eventlet.wsgi import ALREADY_HANDLED
|
||||
_ALREADY_HANDLED = ALREADY_HANDLED
|
||||
from eventlet.wsgi import ALREADY_HANDLED
|
||||
_ALREADY_HANDLED = ALREADY_HANDLED
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
return self._ALREADY_HANDLED
|
||||
@ -124,7 +83,7 @@ class _AlreadyHandledResponse(Response):
|
||||
|
||||
def websocket(name, path):
|
||||
def _websocket(controller_func):
|
||||
def __websocket(self, req, **_):
|
||||
def __websocket(self, req, **kwargs):
|
||||
wrapper = WebSocketRegistrationWrapper(controller_func, self)
|
||||
ws_wsgi = hub.WebSocketWSGI(wrapper)
|
||||
ws_wsgi(req.environ, req.start_response)
|
||||
@ -149,7 +108,6 @@ class ControllerBase(object):
|
||||
def __init__(self, req, link, data, **config):
|
||||
self.req = req
|
||||
self.link = link
|
||||
self.data = data
|
||||
self.parent = None
|
||||
for name, value in config.items():
|
||||
setattr(self, name, value)
|
||||
@ -180,10 +138,10 @@ class WebSocketServerTransport(ServerTransport):
|
||||
if message is None:
|
||||
raise WebSocketDisconnectedError()
|
||||
context = None
|
||||
return context, message
|
||||
return (context, message)
|
||||
|
||||
def send_reply(self, context, reply):
|
||||
self.ws.send(six.text_type(reply))
|
||||
self.ws.send(unicode(reply))
|
||||
|
||||
|
||||
class WebSocketRPCServer(RPCServer):
|
||||
@ -213,7 +171,7 @@ class WebSocketClientTransport(ClientTransport):
|
||||
self.queue = queue
|
||||
|
||||
def send_message(self, message, expect_reply=True):
|
||||
self.ws.send(six.text_type(message))
|
||||
self.ws.send(unicode(message))
|
||||
|
||||
if expect_reply:
|
||||
return self.queue.get()
|
||||
@ -266,15 +224,23 @@ class WSGIApplication(object):
|
||||
self.registory = {}
|
||||
self._wsmanager = WebSocketManager()
|
||||
super(WSGIApplication, self).__init__()
|
||||
# XXX: Switch how to call the API of Routes for every version
|
||||
match_argspec = inspect.getargspec(self.mapper.match)
|
||||
if 'environ' in match_argspec.args:
|
||||
# New API
|
||||
self._match = self._match_with_environ
|
||||
else:
|
||||
# Old API
|
||||
self._match = self._match_with_path_info
|
||||
|
||||
def _match(self, req):
|
||||
# Note: Invoke the new API, first. If the arguments unmatched,
|
||||
# invoke the old API.
|
||||
try:
|
||||
return self.mapper.match(environ=req.environ)
|
||||
except TypeError:
|
||||
self.mapper.environ = req.environ
|
||||
return self.mapper.match(req.path_info)
|
||||
def _match_with_environ(self, req):
|
||||
match = self.mapper.match(environ=req.environ)
|
||||
return match
|
||||
|
||||
def _match_with_path_info(self, req):
|
||||
self.mapper.environ = req.environ
|
||||
match = self.mapper.match(req.path_info)
|
||||
return match
|
||||
|
||||
@wsgify_hack
|
||||
def __call__(self, req, start_response):
|
||||
|
||||
@ -386,7 +386,6 @@ class AppManager(object):
|
||||
self.applications = {}
|
||||
self.contexts_cls = {}
|
||||
self.contexts = {}
|
||||
self.close_sem = hub.Semaphore()
|
||||
|
||||
def load_app(self, name):
|
||||
mod = utils.import_module(name)
|
||||
@ -542,10 +541,7 @@ class AppManager(object):
|
||||
self._close(app)
|
||||
close_dict.clear()
|
||||
|
||||
# This semaphore prevents parallel execution of this function,
|
||||
# as run_apps's finally clause starts another close() call.
|
||||
with self.close_sem:
|
||||
for app_name in list(self.applications.keys()):
|
||||
self.uninstantiate(app_name)
|
||||
assert not self.applications
|
||||
close_all(self.contexts)
|
||||
for app_name in list(self.applications.keys()):
|
||||
self.uninstantiate(app_name)
|
||||
assert not self.applications
|
||||
close_all(self.contexts)
|
||||
|
||||
@ -36,7 +36,6 @@ CONF = oslo_config.cfg.ConfigOpts()
|
||||
|
||||
from oslo_config.cfg import ConfigOpts
|
||||
|
||||
from oslo_config.cfg import Opt
|
||||
from oslo_config.cfg import BoolOpt
|
||||
from oslo_config.cfg import IntOpt
|
||||
from oslo_config.cfg import ListOpt
|
||||
|
||||
@ -16,15 +16,22 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from ryu.lib import hub
|
||||
hub.patch(thread=False)
|
||||
|
||||
from ryu import cfg
|
||||
# TODO:
|
||||
# Right now, we have our own patched copy of ovs python bindings
|
||||
# Once our modification is upstreamed and widely deployed,
|
||||
# use it
|
||||
#
|
||||
# NOTE: this modifies sys.path and thus affects the following imports.
|
||||
import ryu.contrib
|
||||
ryu.contrib.update_module_path()
|
||||
|
||||
from ryu import cfg
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from ryu import log
|
||||
log.early_init_log(logging.DEBUG)
|
||||
|
||||
@ -46,28 +53,10 @@ CONF.register_cli_opts([
|
||||
cfg.BoolOpt('enable-debugger', default=False,
|
||||
help='don\'t overwrite Python standard threading library'
|
||||
'(use only for debugging)'),
|
||||
cfg.StrOpt('user-flags', default=None,
|
||||
help='Additional flags file for user applications'),
|
||||
])
|
||||
|
||||
|
||||
def _parse_user_flags():
|
||||
"""
|
||||
Parses user-flags file and loads it to register user defined options.
|
||||
"""
|
||||
try:
|
||||
idx = list(sys.argv).index('--user-flags')
|
||||
user_flags_file = sys.argv[idx + 1]
|
||||
except (ValueError, IndexError):
|
||||
user_flags_file = ''
|
||||
|
||||
if user_flags_file and os.path.isfile(user_flags_file):
|
||||
from ryu.utils import _import_module_file
|
||||
_import_module_file(user_flags_file)
|
||||
|
||||
|
||||
def main(args=None, prog=None):
|
||||
_parse_user_flags()
|
||||
try:
|
||||
CONF(args=args, prog=prog,
|
||||
project='ryu', version='ryu-manager %s' % version,
|
||||
@ -77,20 +66,21 @@ def main(args=None, prog=None):
|
||||
project='ryu', version='ryu-manager %s' % version)
|
||||
|
||||
log.init_log()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if CONF.enable_debugger:
|
||||
LOG = logging.getLogger('ryu.cmd.manager')
|
||||
msg = 'debugging is available (--enable-debugger option is turned on)'
|
||||
logger.info(msg)
|
||||
LOG.info(msg)
|
||||
else:
|
||||
hub.patch(thread=True)
|
||||
|
||||
if CONF.pid_file:
|
||||
import os
|
||||
with open(CONF.pid_file, 'w') as pid_file:
|
||||
pid_file.write(str(os.getpid()))
|
||||
|
||||
app_lists = CONF.app_lists + CONF.app
|
||||
# keep old behavior, run ofp if no application is specified.
|
||||
# keep old behaivor, run ofp if no application is specified.
|
||||
if not app_lists:
|
||||
app_lists = ['ryu.controller.ofp_handler']
|
||||
|
||||
@ -107,9 +97,6 @@ def main(args=None, prog=None):
|
||||
|
||||
try:
|
||||
hub.joinall(services)
|
||||
except KeyboardInterrupt:
|
||||
logger.debug("Keyboard Interrupt received. "
|
||||
"Closing RYU application manager...")
|
||||
finally:
|
||||
app_mgr.close()
|
||||
|
||||
|
||||
@ -25,15 +25,18 @@
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import cmd
|
||||
import sys
|
||||
|
||||
import lxml.etree as ET
|
||||
from ncclient.operations.rpc import RPCError
|
||||
import ryu.contrib
|
||||
ryu.contrib.update_module_path()
|
||||
|
||||
from ryu import cfg
|
||||
|
||||
import cmd
|
||||
import sys
|
||||
import lxml.etree as ET
|
||||
|
||||
from ryu.lib import of_config
|
||||
from ryu.lib.of_config import capable_switch
|
||||
from ncclient.operations.rpc import RPCError
|
||||
import ryu.lib.of_config.classes as ofc
|
||||
|
||||
|
||||
|
||||
@ -31,25 +31,24 @@
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import ast
|
||||
import ryu.contrib
|
||||
ryu.contrib.update_module_path()
|
||||
|
||||
from ryu import cfg
|
||||
|
||||
import cmd
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import termios
|
||||
|
||||
from ryu import cfg
|
||||
from ryu.lib import rpc
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_cli_opts([
|
||||
cfg.ListOpt('peers', default=[],
|
||||
help='List of peers, separated by commas. '
|
||||
'(e.g., "hoge=localhost:9998,fuga=localhost:9999")'),
|
||||
cfg.StrOpt('command', short='c', default=None,
|
||||
help='Command to be executed as single command. '
|
||||
'The default is None and opens interactive console.'),
|
||||
# eg. rpc-cli --peers=hoge=localhost:9998,fuga=localhost:9999
|
||||
cfg.ListOpt('peers', default=[], help='list of peers')
|
||||
])
|
||||
|
||||
|
||||
@ -57,18 +56,16 @@ class Peer(object):
|
||||
def __init__(self, name, addr):
|
||||
self._name = name
|
||||
self._addr = addr
|
||||
self.socket = None
|
||||
self.client = None
|
||||
try:
|
||||
self.connect()
|
||||
except ConnectionError as e:
|
||||
print('Exception when connecting to peer "%s": %s' % (name, e))
|
||||
raise e
|
||||
except:
|
||||
pass
|
||||
|
||||
def connect(self):
|
||||
self.socket = socket.create_connection(self._addr)
|
||||
self.client = rpc.Client(self.socket,
|
||||
notification_callback=self.notification)
|
||||
self.client = None
|
||||
s = socket.create_connection(self._addr)
|
||||
self.client = rpc.Client(s, notification_callback=self.notification)
|
||||
|
||||
def try_to_connect(self, verbose=False):
|
||||
if self.client:
|
||||
@ -107,25 +104,12 @@ class Peer(object):
|
||||
print("connected. retrying the request...")
|
||||
return g()
|
||||
|
||||
def close(self):
|
||||
self.socket.close()
|
||||
|
||||
|
||||
peers = {}
|
||||
|
||||
|
||||
def add_peer(name, host, port):
|
||||
try:
|
||||
peer = Peer(name, (host, port))
|
||||
except ConnectionError:
|
||||
return
|
||||
|
||||
peers[name] = peer
|
||||
|
||||
|
||||
def close_peers():
|
||||
for peer in peers.values():
|
||||
peer.socket.close()
|
||||
peers[name] = Peer(name, (host, port))
|
||||
|
||||
|
||||
class Cmd(cmd.Cmd):
|
||||
@ -140,9 +124,9 @@ class Cmd(cmd.Cmd):
|
||||
try:
|
||||
peer = args[0]
|
||||
method = args[1]
|
||||
params = ast.literal_eval(args[2])
|
||||
except (IndexError, ValueError) as e:
|
||||
print("argument error: %s" % e)
|
||||
params = eval(args[2])
|
||||
except:
|
||||
print("argument error")
|
||||
return
|
||||
try:
|
||||
p = peers[peer]
|
||||
@ -190,8 +174,7 @@ class Cmd(cmd.Cmd):
|
||||
def complete_notify(self, text, line, begidx, endidx):
|
||||
return self._complete_peer(text, line, begidx, endidx)
|
||||
|
||||
def do_EOF(self, _line=None):
|
||||
close_peers()
|
||||
def do_EOF(self, _line):
|
||||
sys.exit(0)
|
||||
|
||||
def emptyline(self):
|
||||
@ -222,9 +205,6 @@ class Cmd(cmd.Cmd):
|
||||
signal.signal(signal.SIGALRM, self._timeout)
|
||||
signal.alarm(1)
|
||||
|
||||
def postloop(self):
|
||||
close_peers()
|
||||
|
||||
def onecmd(self, string):
|
||||
self._in_onecmd = True
|
||||
try:
|
||||
@ -253,11 +233,6 @@ def main(args=None, prog=None):
|
||||
host, port = addr.rsplit(':', 1)
|
||||
add_peer(name, host, port)
|
||||
|
||||
if CONF.command:
|
||||
command = Cmd()
|
||||
command.onecmd(CONF.command)
|
||||
command.do_EOF()
|
||||
|
||||
Cmd().cmdloop()
|
||||
|
||||
|
||||
|
||||
@ -14,13 +14,15 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import argparse
|
||||
import os.path
|
||||
import sys
|
||||
import ryu.contrib
|
||||
ryu.contrib.update_module_path()
|
||||
|
||||
from ryu import cfg
|
||||
from ryu import utils
|
||||
from ryu import version
|
||||
import argparse
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
|
||||
subcommands = {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
|
||||
# Copyright 2009 Shikhar Bhushan
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -8,13 +8,15 @@
|
||||
#
|
||||
# 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.
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Client implementation for Zebra protocol service.
|
||||
import sys
|
||||
|
||||
This module provides the client side implementation for Zebra protocol.
|
||||
"""
|
||||
if sys.version_info < (2, 6):
|
||||
raise RuntimeError('You need Python 2.6+ for this module.')
|
||||
|
||||
class NCClientError(Exception):
|
||||
"Base type for all NCClient errors"
|
||||
pass
|
||||
68
ryu/contrib/ncclient/capabilities.py
Normal file
68
ryu/contrib/ncclient/capabilities.py
Normal file
@ -0,0 +1,68 @@
|
||||
# Copyright 2009 Shikhar Bhushan
|
||||
#
|
||||
# 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.
|
||||
|
||||
def _abbreviate(uri):
|
||||
if uri.startswith("urn:ietf:params") and ":netconf:" in uri:
|
||||
splitted = uri.split(":")
|
||||
if ":capability:" in uri:
|
||||
if uri.startswith("urn:ietf:params:xml:ns:netconf"):
|
||||
name, version = splitted[7], splitted[8]
|
||||
else:
|
||||
name, version = splitted[5], splitted[6]
|
||||
return [ ":" + name, ":" + name + ":" + version ]
|
||||
elif ":base:" in uri:
|
||||
if uri.startswith("urn:ietf:params:xml:ns:netconf"):
|
||||
return [ ":base", ":base" + ":" + splitted[7] ]
|
||||
else:
|
||||
return [ ":base", ":base" + ":" + splitted[5] ]
|
||||
return []
|
||||
|
||||
def schemes(url_uri):
|
||||
"Given a URI that has a *scheme* query string (i.e. `:url` capability URI), will return a list of supported schemes."
|
||||
return url_uri.partition("?scheme=")[2].split(",")
|
||||
|
||||
class Capabilities:
|
||||
|
||||
"Represents the set of capabilities available to a NETCONF client or server. It is initialized with a list of capability URI's."
|
||||
|
||||
def __init__(self, capabilities):
|
||||
self._dict = {}
|
||||
for uri in capabilities:
|
||||
self._dict[uri] = _abbreviate(uri)
|
||||
|
||||
def __contains__(self, key):
|
||||
if key in self._dict:
|
||||
return True
|
||||
for abbrs in self._dict.values():
|
||||
if key in abbrs:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __len__(self):
|
||||
return len(self._dict)
|
||||
|
||||
def __iter__(self):
|
||||
return self._dict.iterkeys()
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self._dict.keys())
|
||||
|
||||
def add(self, uri):
|
||||
"Add a capability."
|
||||
self._dict[uri] = _abbreviate(uri)
|
||||
|
||||
def remove(self, uri):
|
||||
"Remove a capability."
|
||||
if key in self._dict:
|
||||
del self._dict[key]
|
||||
24
ryu/contrib/ncclient/debug.py
Normal file
24
ryu/contrib/ncclient/debug.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Copyright 2009 Shikhar Bhushan
|
||||
#
|
||||
# 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 ncclient.transport import SessionListener
|
||||
|
||||
class PrintListener(SessionListener):
|
||||
|
||||
def callback(self, root, raw):
|
||||
print('\n# RECEIVED MESSAGE with root=[tag=%r, attrs=%r] #\n%r\n' %
|
||||
(root[0], root[1], raw))
|
||||
|
||||
def errback(self, err):
|
||||
print('\n# RECEIVED ERROR #\n%r\n' % err)
|
||||
177
ryu/contrib/ncclient/manager.py
Normal file
177
ryu/contrib/ncclient/manager.py
Normal file
@ -0,0 +1,177 @@
|
||||
# Copyright 2009 Shikhar Bhushan
|
||||
# Copyright 2011 Leonidas Poulopoulos
|
||||
#
|
||||
# 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 is a thin layer of abstraction around the library. It exposes all core functionality."""
|
||||
|
||||
import capabilities
|
||||
import operations
|
||||
import transport
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('ncclient.manager')
|
||||
|
||||
CAPABILITIES = [
|
||||
"urn:ietf:params:netconf:base:1.0",
|
||||
"urn:ietf:params:netconf:capability:writable-running:1.0",
|
||||
"urn:ietf:params:netconf:capability:candidate:1.0",
|
||||
"urn:ietf:params:netconf:capability:confirmed-commit:1.0",
|
||||
"urn:ietf:params:netconf:capability:rollback-on-error:1.0",
|
||||
"urn:ietf:params:netconf:capability:startup:1.0",
|
||||
"urn:ietf:params:netconf:capability:url:1.0?scheme=http,ftp,file,https,sftp",
|
||||
"urn:ietf:params:netconf:capability:validate:1.0",
|
||||
"urn:ietf:params:netconf:capability:xpath:1.0",
|
||||
"urn:liberouter:params:netconf:capability:power-control:1.0",
|
||||
"urn:ietf:params:netconf:capability:interleave:1.0"
|
||||
]
|
||||
"""A list of URI's representing the client's capabilities. This is used during the initial capability exchange. Modify this if you need to announce some capability not already included."""
|
||||
|
||||
OPERATIONS = {
|
||||
"get": operations.Get,
|
||||
"get_config": operations.GetConfig,
|
||||
"dispatch": operations.Dispatch,
|
||||
"edit_config": operations.EditConfig,
|
||||
"copy_config": operations.CopyConfig,
|
||||
"validate": operations.Validate,
|
||||
"commit": operations.Commit,
|
||||
"discard_changes": operations.DiscardChanges,
|
||||
"delete_config": operations.DeleteConfig,
|
||||
"lock": operations.Lock,
|
||||
"unlock": operations.Unlock,
|
||||
"close_session": operations.CloseSession,
|
||||
"kill_session": operations.KillSession,
|
||||
"poweroff_machine": operations.PoweroffMachine,
|
||||
"reboot_machine": operations.RebootMachine
|
||||
}
|
||||
"""Dictionary of method names and corresponding :class:`~ncclient.operations.RPC` subclasses. It is used to lookup operations, e.g. `get_config` is mapped to :class:`~ncclient.operations.GetConfig`. It is thus possible to add additional operations to the :class:`Manager` API."""
|
||||
|
||||
def connect_ssh(*args, **kwds):
|
||||
"""Initialize a :class:`Manager` over the SSH transport. For documentation of arguments see :meth:`ncclient.transport.SSHSession.connect`.
|
||||
|
||||
The underlying :class:`ncclient.transport.SSHSession` is created with :data:`CAPABILITIES`. It is first instructed to :meth:`~ncclient.transport.SSHSession.load_known_hosts` and then all the provided arguments are passed directly to its implementation of :meth:`~ncclient.transport.SSHSession.connect`.
|
||||
"""
|
||||
session = transport.SSHSession(capabilities.Capabilities(CAPABILITIES))
|
||||
session.load_known_hosts()
|
||||
session.connect(*args, **kwds)
|
||||
return Manager(session)
|
||||
|
||||
connect = connect_ssh
|
||||
"Same as :func:`connect_ssh`, since SSH is the default (and currently, the only) transport."
|
||||
|
||||
class OpExecutor(type):
|
||||
|
||||
def __new__(cls, name, bases, attrs):
|
||||
def make_wrapper(op_cls):
|
||||
def wrapper(self, *args, **kwds):
|
||||
return self.execute(op_cls, *args, **kwds)
|
||||
wrapper.func_doc = op_cls.request.func_doc
|
||||
return wrapper
|
||||
for op_name, op_cls in OPERATIONS.iteritems():
|
||||
attrs[op_name] = make_wrapper(op_cls)
|
||||
return super(OpExecutor, cls).__new__(cls, name, bases, attrs)
|
||||
|
||||
class Manager(object):
|
||||
|
||||
"""For details on the expected behavior of the operations and their parameters refer to :rfc:`4741`.
|
||||
|
||||
Manager instances are also context managers so you can use it like this::
|
||||
|
||||
with manager.connect("host") as m:
|
||||
# do your stuff
|
||||
|
||||
... or like this::
|
||||
|
||||
m = manager.connect("host")
|
||||
try:
|
||||
# do your stuff
|
||||
finally:
|
||||
m.close_session()
|
||||
"""
|
||||
|
||||
__metaclass__ = OpExecutor
|
||||
|
||||
def __init__(self, session, timeout=30):
|
||||
self._session = session
|
||||
self._async_mode = False
|
||||
self._timeout = timeout
|
||||
self._raise_mode = operations.RaiseMode.ALL
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.close_session()
|
||||
return False
|
||||
|
||||
def __set_timeout(self, timeout):
|
||||
self._timeout = timeout
|
||||
|
||||
def __set_async_mode(self, mode):
|
||||
self._async_mode = mode
|
||||
|
||||
def __set_raise_mode(self, mode):
|
||||
assert(mode in (operations.RaiseMode.NONE, operations.RaiseMode.ERRORS, operations.RaiseMode.ALL))
|
||||
self._raise_mode = mode
|
||||
|
||||
def execute(self, cls, *args, **kwds):
|
||||
return cls(self._session,
|
||||
async=self._async_mode,
|
||||
timeout=self._timeout,
|
||||
raise_mode=self._raise_mode).request(*args, **kwds)
|
||||
|
||||
def locked(self, target):
|
||||
"""Returns a context manager for a lock on a datastore, where *target* is the name of the configuration datastore to lock, e.g.::
|
||||
|
||||
with m.locked("running"):
|
||||
# do your stuff
|
||||
|
||||
... instead of::
|
||||
|
||||
m.lock("running")
|
||||
try:
|
||||
# do your stuff
|
||||
finally:
|
||||
m.unlock("running")
|
||||
"""
|
||||
return operations.LockContext(self._session, target)
|
||||
|
||||
@property
|
||||
def client_capabilities(self):
|
||||
":class:`~ncclient.capabilities.Capabilities` object representing the client's capabilities."
|
||||
return self._session._client_capabilities
|
||||
|
||||
@property
|
||||
def server_capabilities(self):
|
||||
":class:`~ncclient.capabilities.Capabilities` object representing the server's capabilities."
|
||||
return self._session._server_capabilities
|
||||
|
||||
@property
|
||||
def session_id(self):
|
||||
"`session-id` assigned by the NETCONF server."
|
||||
return self._session.id
|
||||
|
||||
@property
|
||||
def connected(self):
|
||||
"Whether currently connected to the NETCONF server."
|
||||
return self._session.connected
|
||||
|
||||
async_mode = property(fget=lambda self: self._async_mode, fset=__set_async_mode)
|
||||
"Specify whether operations are executed asynchronously (`True`) or synchronously (`False`) (the default)."
|
||||
|
||||
timeout = property(fget=lambda self: self._timeout, fset=__set_timeout)
|
||||
"Specify the timeout for synchronous RPC requests."
|
||||
|
||||
raise_mode = property(fget=lambda self: self._raise_mode, fset=__set_raise_mode)
|
||||
"Specify which errors are raised as :exc:`~ncclient.operations.RPCError` exceptions. Valid values are the constants defined in :class:`~ncclient.operations.RaiseMode`. The default value is :attr:`~ncclient.operations.RaiseMode.ALL`."
|
||||
51
ryu/contrib/ncclient/operations/__init__.py
Normal file
51
ryu/contrib/ncclient/operations/__init__.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Copyright 2009 Shikhar Bhushan
|
||||
#
|
||||
# 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 errors import OperationError, TimeoutExpiredError, MissingCapabilityError
|
||||
from rpc import RPC, RPCReply, RPCError, RaiseMode
|
||||
|
||||
# rfc4741 ops
|
||||
from retrieve import Get, GetConfig, GetReply, Dispatch
|
||||
from edit import EditConfig, CopyConfig, DeleteConfig, Validate, Commit, DiscardChanges
|
||||
from session import CloseSession, KillSession
|
||||
from lock import Lock, Unlock, LockContext
|
||||
# others...
|
||||
from flowmon import PoweroffMachine, RebootMachine
|
||||
|
||||
__all__ = [
|
||||
'RPC',
|
||||
'RPCReply',
|
||||
'RPCError',
|
||||
'RaiseMode',
|
||||
'Get',
|
||||
'GetConfig',
|
||||
'Dispatch',
|
||||
'GetReply',
|
||||
'EditConfig',
|
||||
'CopyConfig',
|
||||
'Validate',
|
||||
'Commit',
|
||||
'DiscardChanges',
|
||||
'DeleteConfig',
|
||||
'Lock',
|
||||
'Unlock',
|
||||
'PoweroffMachine',
|
||||
'RebootMachine',
|
||||
'LockContext',
|
||||
'CloseSession',
|
||||
'KillSession',
|
||||
'OperationError',
|
||||
'TimeoutExpiredError',
|
||||
'MissingCapabilityError'
|
||||
]
|
||||
143
ryu/contrib/ncclient/operations/edit.py
Normal file
143
ryu/contrib/ncclient/operations/edit.py
Normal file
@ -0,0 +1,143 @@
|
||||
# Copyright 2009 Shikhar Bhushan
|
||||
#
|
||||
# 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 ncclient.xml_ import *
|
||||
|
||||
from rpc import RPC
|
||||
|
||||
import util
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("ncclient.operations.edit")
|
||||
|
||||
"Operations related to changing device configuration"
|
||||
|
||||
class EditConfig(RPC):
|
||||
"`edit-config` RPC"
|
||||
|
||||
def request(self, target, config, default_operation=None, test_option=None, error_option=None):
|
||||
"""Loads all or part of the specified *config* to the *target* configuration datastore.
|
||||
|
||||
*target* is the name of the configuration datastore being edited
|
||||
|
||||
*config* is the configuration, which must be rooted in the `config` element. It can be specified either as a string or an :class:`~xml.etree.ElementTree.Element`.
|
||||
|
||||
*default_operation* if specified must be one of { `"merge"`, `"replace"`, or `"none"` }
|
||||
|
||||
*test_option* if specified must be one of { `"test_then_set"`, `"set"` }
|
||||
|
||||
*error_option* if specified must be one of { `"stop-on-error"`, `"continue-on-error"`, `"rollback-on-error"` }
|
||||
|
||||
The `"rollback-on-error"` *error_option* depends on the `:rollback-on-error` capability.
|
||||
"""
|
||||
node = new_ele("edit-config")
|
||||
node.append(util.datastore_or_url("target", target, self._assert))
|
||||
if error_option is not None:
|
||||
if error_option == "rollback-on-error":
|
||||
self._assert(":rollback-on-error")
|
||||
sub_ele(node, "error-option").text = error_option
|
||||
if test_option is not None:
|
||||
self._assert(':validate')
|
||||
sub_ele(node, "test-option").text = test_option
|
||||
if default_operation is not None:
|
||||
# TODO: check if it is a valid default-operation
|
||||
sub_ele(node, "default-operation").text = default_operation
|
||||
node.append(validated_element(config, ("config", qualify("config"))))
|
||||
return self._request(node)
|
||||
|
||||
|
||||
class DeleteConfig(RPC):
|
||||
"`delete-config` RPC"
|
||||
|
||||
def request(self, target):
|
||||
"""Delete a configuration datastore.
|
||||
|
||||
*target* specifies the name or URL of configuration datastore to delete
|
||||
|
||||
:seealso: :ref:`srctarget_params`"""
|
||||
node = new_ele("delete-config")
|
||||
node.append(util.datastore_or_url("target", target, self._assert))
|
||||
return self._request(node)
|
||||
|
||||
|
||||
class CopyConfig(RPC):
|
||||
"`copy-config` RPC"
|
||||
|
||||
def request(self, source, target):
|
||||
"""Create or replace an entire configuration datastore with the contents of another complete
|
||||
configuration datastore.
|
||||
|
||||
*source* is the name of the configuration datastore to use as the source of the copy operation or `config` element containing the configuration subtree to copy
|
||||
|
||||
*target* is the name of the configuration datastore to use as the destination of the copy operation
|
||||
|
||||
:seealso: :ref:`srctarget_params`"""
|
||||
node = new_ele("copy-config")
|
||||
node.append(util.datastore_or_url("target", target, self._assert))
|
||||
node.append(util.datastore_or_url("source", source, self._assert))
|
||||
return self._request(node)
|
||||
|
||||
|
||||
class Validate(RPC):
|
||||
"`validate` RPC. Depends on the `:validate` capability."
|
||||
|
||||
DEPENDS = [':validate']
|
||||
|
||||
def request(self, source):
|
||||
"""Validate the contents of the specified configuration.
|
||||
|
||||
*source* is the name of the configuration datastore being validated or `config` element containing the configuration subtree to be validated
|
||||
|
||||
:seealso: :ref:`srctarget_params`"""
|
||||
node = new_ele("validate")
|
||||
try:
|
||||
src = validated_element(source, ("config", qualify("config")))
|
||||
except Exception as e:
|
||||
logger.debug(e)
|
||||
src = util.datastore_or_url("source", source, self._assert)
|
||||
(node if src.tag == "source" else sub_ele(node, "source")).append(src)
|
||||
return self._request(node)
|
||||
|
||||
|
||||
class Commit(RPC):
|
||||
"`commit` RPC. Depends on the `:candidate` capability, and the `:confirmed-commit`."
|
||||
|
||||
DEPENDS = [':candidate']
|
||||
|
||||
def request(self, confirmed=False, timeout=None):
|
||||
"""Commit the candidate configuration as the device's new current configuration. Depends on the `:candidate` capability.
|
||||
|
||||
A confirmed commit (i.e. if *confirmed* is `True`) is reverted if there is no followup commit within the *timeout* interval. If no timeout is specified the confirm timeout defaults to 600 seconds (10 minutes). A confirming commit may have the *confirmed* parameter but this is not required. Depends on the `:confirmed-commit` capability.
|
||||
|
||||
*confirmed* whether this is a confirmed commit
|
||||
|
||||
*timeout* specifies the confirm timeout in seconds"""
|
||||
node = new_ele("commit")
|
||||
if confirmed:
|
||||
self._assert(":confirmed-commit")
|
||||
sub_ele(node, "confirmed")
|
||||
if timeout is not None:
|
||||
sub_ele(node, "confirm-timeout").text = timeout
|
||||
return self._request(node)
|
||||
|
||||
|
||||
class DiscardChanges(RPC):
|
||||
"`discard-changes` RPC. Depends on the `:candidate` capability."
|
||||
|
||||
DEPENDS = [":candidate"]
|
||||
|
||||
def request(self):
|
||||
"""Revert the candidate configuration to the currently running configuration. Any uncommitted changes are discarded."""
|
||||
return self._request(new_ele("discard-changes"))
|
||||
@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
|
||||
# Copyright 2009 Shikhar Bhushan
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -8,13 +8,17 @@
|
||||
#
|
||||
# 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.
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Server implementation for Zebra protocol service.
|
||||
from ncclient import NCClientError
|
||||
|
||||
This module provides the server side implementation for Zebra protocol.
|
||||
"""
|
||||
class OperationError(NCClientError):
|
||||
pass
|
||||
|
||||
class TimeoutExpiredError(NCClientError):
|
||||
pass
|
||||
|
||||
class MissingCapabilityError(NCClientError):
|
||||
pass
|
||||
39
ryu/contrib/ncclient/operations/flowmon.py
Normal file
39
ryu/contrib/ncclient/operations/flowmon.py
Normal file
@ -0,0 +1,39 @@
|
||||
# Copyright 2h009 Shikhar Bhushan
|
||||
#
|
||||
# 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.
|
||||
|
||||
'Power-control operations'
|
||||
|
||||
from ncclient.xml_ import *
|
||||
|
||||
from rpc import RPC
|
||||
|
||||
PC_URN = "urn:liberouter:params:xml:ns:netconf:power-control:1.0"
|
||||
|
||||
class PoweroffMachine(RPC):
|
||||
|
||||
"*poweroff-machine* RPC (flowmon)"
|
||||
|
||||
DEPENDS = ["urn:liberouter:param:netconf:capability:power-control:1.0"]
|
||||
|
||||
def request(self):
|
||||
return self._request(new_ele(qualify("poweroff-machine", PC_URN)))
|
||||
|
||||
class RebootMachine(RPC):
|
||||
|
||||
"*reboot-machine* RPC (flowmon)"
|
||||
|
||||
DEPENDS = ["urn:liberouter:params:netconf:capability:power-control:1.0"]
|
||||
|
||||
def request(self):
|
||||
return self._request(new_ele(qualify("reboot-machine", PC_URN)))
|
||||
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